Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
C Craft: C is the desert island language. (stanford.edu)
188 points by nkurz on April 7, 2011 | hide | past | favorite | 109 comments


>In my Eiffel days, I was encouraged to write "integer", not "int", "character", not "char", and so on. I believe Java encourages this practice too. Supposedly clarity is maximized. But how can this be if we do the opposite when we speak? Do you say “taxi cab”, or “taximeter cabriolet”? Redundant utterances are tiresome and worse still, obscure the idea being expressed.

I see this argument a lot, and they strike me as people complaining because they don't use tools their language provides.

Typedefs are the answer to excessive name length, and they're nearly everywhere. Just create a couple typedef files, and import them as needed - future programmers get the full names easily, while you can program in your pseudo-K version of C for maximum keyboard efficiency. I have a handful of such files, they're endlessly useful - why write `Pop_froM_lIsT_WhIch_COntaiNs_speCiFik_type_X`, doing battle with naming-scheme-X that only employee-Y uses (and their associated spelling errors) when you can do so once, and write `pop` from then on, unambiguously?

The upside of typedefs for this comparison is that they're precisely what we do with spoken language - nobody knew what a "taxi cab" was until someone told them it the shorter version of "taximeter cabriolet", or until the full phrase was well enough known that it could be inferred accurately by the average person.


Given that Java doesn't have typedefs how would you reduce the verbosity of : ConcurrentHashMap<String, Integer> foo = new ConcurrentHashMap<String, Integer>();


Do something like the Google Collections Library does:

    HashMap<String, Integer> foo = Maps.newHashMap();
The return type of newHashMap is determined by the type of foo, so you don't need to repeat the generic types in angle brackets.

  public static <K, V> HashMap<K, V> newHashMap() {
    return new HashMap<K, V>();
  }
http://google-collections.googlecode.com/svn/trunk/javadoc/i...

(I'm not a fan of Java myself, but there are some tricky ways to cut down on its verbosity)


The Collections class built into Java has a lot of timesavers like this too--singleton, singleonList, singletonMap, and Arrays has stuff like Arrays.asList( ... ).

Also it's rarely important that you know the actual type of Map you're using (the interface is what's important), so you can just call

Map<String, Integer> foo = Maps.newHashMap();


There you're largely screwed, but Java is well-known for being extremely verbose, and of course there will be languages that don't have basic features others have had for decades.

The closest you can get in Java is to wrap it in a private, internal class. Which also lets you rename some verbose or frequently-combined operations, say:

  foo.someoneCouldntComeUpWithAShorterName() -> wrapped.succinct()
  -- or --
  foo.store(val);
  foo.store(val);
  foo.store(val); // I hate void functions...
  -> wrapped.store(val,val,val);
But yes, it's a pain. Java's largely a pain.


I see your Java, and raise you Objective-C:

    NSString *thing = [NSString stringWithFormat:@"Result: %@", [foo someoneCouldntComeUpWithAShorterNameWithZipcode:08205 name:@"larry" parcels:5]];
    NSDictionary *request = [NSDictionary dictionaryWithObjectsAndKeys:@"iphoneapp/0.1", @"User-Agent", @"news.ycombinator.com", @"Host", nil];
This isn't to kick off pet language wars, by the way, just to point out that my OCD tic to wrap to 80 characters has absolutely zero place in Xcode. Or Java.


While it can get a bit silly sometimes, I've come to really appreciate the verbosity of Obj-C method calls. Explicit parameter names make them very self-descriptive, and grouping the entire call within brackets makes structure obvious. Compare:

  canvas.drawImage(kittens.subImage(vec2(10,10),vec2(20,20)),vec2(30,30))
vs

  [canvas drawImage:[kittens subImageAt:vec2(10,10) withSize:vec2(20,20)] at:vec2(30,30)]


Yes but some of the examples of obj-c I've seen look ridiculously wordy compared with the smalltalk equivalents (even if you ignore op overloading). (setObject: forKey:) vs (at: put:). for ex.


disclaimer: I have not tried these. Totally pulling these from a quick googled-refresher of macros.

  #define withZip someoneCouldntComeUpWithAShorterNameWithZipcode 
  #define dictWith dictionaryWithObjectsAndKeys
  or 
  #define dictWith NSDictionary dictionaryWithObjectsAndKeys
  so you can [dictWith: key, value, key, value]
:) I forget, does XCode intelligently handle macros? Or does using a macro destroy its helpful features, like code completion?

I actually have a "hashWith" utility function for my .NET code. Saves me a ton of ugh-induced headaches.


Doing that is probably fine if you are working alone and nobody will ever read your code. Reading Objective-C is already enough of a challenge without having to context switch to a header just to mentally resolve symbols.

Instead of using a macro for the selector:

    NSDictionary *frob = [NSDictionary macroHere:v1, k1, v0, k0, nil];
It would probably be far more efficient to macro the entire call using C99's variadics:

    NSDictionary *frob = DICT(v1, k1, v0, k0);
Still wouldn't walk that road, personally.


The reason I went for the selector is because it makes the macro into essentially a function-typedef. I don't know any languages which allow typedefs to specify things like `Class.short = Class.super_long_name_here`, even though it's essentially the same concept, especially if looked at from a C standpoint. It's a minimally-intelligent text replacement.

IMO, something like `[NSDictionary with:key,val,key,val,nil]` is far less brain-taxing than `DICT(key,val,key,val)`. It fits without modifying the language or the syntax, it merely makes something long shorter, and the usage implies it does nothing "shiny" - you even keep the class name you're calling, making it clearer what you should be expecting, and it still allows access to all other parameters without modification or even thinking differently. DICT(...) is ambiguous about what kind of dictionary it's returning.


Yup, macros are a bad idea in that regard. Had to work on a codebase where the prior programmer had fancy defs like _dict k1, v1, k2, v2, k3, v3 dict_

I wasn't too happy about that :/


I haven't worked a lot with XCode, but I remember using macros without problem.


"just to point out that my OCD tic to wrap to 80 characters has absolutely zero place in Xcode. Or Java."

Dude. Are you still using CGA? Unless for your IDE you're running emacs on your wristwatch or phone there's no place for that shit in the real world, and you have no excuse.

Ditch that green cathode ray tube, here, have 50c and go buy yourself a real monitor. :D


Lines longer than 80 chars are harder to scan, and sticking to this rule allows tiling of windows. Maximising code windows on a widescreen is a huge waste. Even with long lines the average line length will be far shorter.

This is Guido's reasoning for keeping PEP8's rule to 80 chars.


I work on a 27" iMac with a 27" second monitor, and a third 19" on a Linux box with Synergy. My primary language is Python, so I do have a place for that shit in the real world.


It is fascinating to see people having 24+" monitors -- and seeing ca 30 lines of code on their screen, because of the real estate eaten by their IDEs.

To add maiming to injury, these guys write in modern Cobol (a.k.a Java).

Not to mention that they can't print the code -- since it is 150+ chars wide(!) -- and browse it at a cafe. (I love my iPad, but It'll be iPad 3 before it is half as nice for browsing code [edit: as paper].)

I have about 2 full pages of code on the same size of monitor. Of scripting language. I doubt that I have abnormally bad memory or write too complex code... I just like to be productive.

Edit: oK, downvote if you want. But please also add a counter argument for wasting all that screen area?


"Not to mention that they can't print the code -- since it is 150+ chars wide(!) -- and browse it at a cafe."

You can't print code longer than 150 characters wide?

Since when?


Sigh, are you trolling or new? OK, I'll answer.

The point of printing and browsing code is that it is easier and nicer than to read it on a screen (you'll have a portable with you at the cafe anyway to search, annotate, etc.) If you get lots of broken lines, it isn't readable.

Point was, you lose a capability with long lines -- a nice way to go over code. This is important for many of us.

Edit: Point jedsmith, write it off as grumpy-old-man syndrome. Sigh, I just can't get that this isn't understood by everyone, anymore.


Broken lines?

We no longer live in the days of typewriters or daisychain printers with fixed column widths.

We now have these wonderful things called laser printers which can print using fonts.

Fonts are resizeable. Even to the point that 150+ columns could be fit on one unbroken line.

And even lines that long can be quite readable if you print in landscape.


Let me first not that printing was a little sub point.

Second, in what you quote from: easier and nicer than to read it on a screen.

So your argument seems to be that it is equally nice to read fewer lines of paper in a smaller font. Huh? Also, my eyes can't handle 4 pages on a page any more -- and your eyes won't be able to do that after 40, either.


You could have made that point without the personal attack.


Yeah, and congrats, now you have to hire people who already know what a foo is.

The great thing about ConcurrentHashMap<Long,String> isn't writing it, it's reading it. Even if you're not a Java programmer you know exactly what that is (and FWIW it's world-class on implementation). We could do with some syntactical sugar to make that constructor line a little simpler, but declaring the type and calling a specific constructor, not masking either with personal shorthand are both Good Things if someone's going to have to read that code later.


I am skeptical of the claim that verbosity for its own sake aids comprehension. To borrow an example from Rob Pike, reading

  buf[thisVariableIsTheLoopIndex] = foo(...);
is no more obvious than

  buf[i] = foo(...);
and indeed is more likely to slow the programmer down by killing his train of thought and mental flow.


The problem with that example is how contrived and narrow it is. All it proves is that verbosity doesn't aid loop indices.

ConcurrentHashMap<K, V>, on the other hand, says everything it needs to in the name. As does NSMutableArray, and so on. Apples and oranges, here.


Personally, I find "thisVariableIsTheLoopIndex" to be a terrible loop index variable name. But "i" isn't great either. "index" seems to be closer to the sweet spot to me. Or "fooIndex" and "barIndex" if loops over foo and bar collections are both in play. That beats the hell out or trying to parse "i" and j" as indexes over two collections.

The simple rule is that the larger the scope of the variable the longer the name - i.e. the more widely references to the var occur, the further they are from the definition, the more descriptive the name has to be.


So I suppose this leads Rob Pike to use one-character names for all the variables and functions in his programs?

Such programs must be a joy to read.


His code actually _is_ a joy to read and use. Some of it requires more than a passing scan. But that's true of any code worth reading - if you don't need to scan it twice, it's most probably useless and bureaucratic, and could be done away with if a better design is used.


I very much doubt his code is both a joy to read and made up exclusively of one-character variable and function names.

It's one or the other.


His local indices are usually one letter (i, j, k), his local counters are usually one letter (m, n). His accumulators are sometimes one letter - much like math expressions.

He gives a more descriptive names to variables that live for more than a 10 lines or code or so.

And that's way more of a joy than reading a Java sourceful of "indexThatGoesSourceThe2ndArray"


I really don't get what you're driving at here.

Are you claiming there's a java language requirement that array indices are named with more than one letter? That's not what the screen in front of me says.

"He gives a more descriptive names to variables that live for more than a 10 lines or code or so."

Ok, now we're talking. I like go and Rob Pike's reputation speaks for itself but don't put words in the guy's mouth, go has strong typing in a way that's much more similar to Java than whatever your pet language is, the major difference is syntactical sugar that infers types more often, and a looser contract for interface (both of which I agree with).


> Are you claiming there's a java language requirement that array indices are named with more than one letter?

No, but it _is_ common practice to name things with long names, rather than one letter, even if they are loop indices. E.g., in the FLTK source code, most objects inside a short loop are called "o", e.g., things like

   for (Widget o = window->first(); o; o = o->next()) {
     o->activate();
     o->show();
   }
I think that is extremely readable, 'o' in my mind stands for object in the same way that 'i' stands for index. Have you ever seen Java code in the wild that follows such a convention? I haven't -- it's always things like theWidget or even theChosenWidget instead.

> I like go and Rob Pike's reputation speaks for itself but don't put words in the guy's mouth

Please don't put anything in my mouth. I'm paraphrasing pike, you may disagree with the paraphrasing, but I was referring to, http://www.lysator.liu.se/c/pikestyle.html , specifically the section titled "Variable names". It's about C, and predates Go by some 18 years (and Java by 6). Not any "pet language" of mine.


Ok, well I was talking about type systems. You were talking about the fact that it's dumb to have long variable names for loop indices.

I agree. Everyone on earth agrees with you. I don't know if I've ever in my whole career seen a >1 character loop index variable name. Congrats.


Close. Which is what I don't like about the samples that I have seen in his Go language. Code like "Fmt.PrintF" seems a step backwards to me.


I think the verbosity complaint here isn't about the length of the name, it's about having to repeat the type and generic arguments.


Yeah, repeating the generics is cumbersome and we could do with a better syntactical sugar for that.

Typically, the type's only reapeated if you're assigning to the same exact type as your constructor. In most cases, Map<K,V> foo = new ConcurrentHashMap<K,V> is more idiomatic than typing ConcurrentHashMap twice. Although concurrent is a little special because it's an implementation detail that you sometimes want to enforce via types.


Doesn't Java have any kind of type inference for variables yet? C# allows you to say

  var foo = new Dictionary<string, int>();
and foo's type is infered as Dictionary<string, int>


I would argue that you don't really need to, since auto-completing code editors and displays with enough resolution to support more than 80 columns are pretty ubiquitous.


Just declare a new class with a short name that extends ConcurrentHashMap<String, Integer>.


I'd write the code in Python (or indeed any sensible modern language).

   foo = {}


What if you want to create an instance of other type of dictionary? {} works only for built-in dicts.


and then you get an int instead of a string and your python app explodes. let's not start on "sensible modern" languages.


Yes, you do get bugs in Python due to variables being the wrong type... however the time it takes to fix them is vastly less than the time it takes to write the reams of boilerplate that Java requires (not to mention the cognitive overload caused by said reams of boilerplate).

My ideal would be an optionally typed language.


I don't think it is an issue of technology (typedef, etc...) as much as an application target issue. Using short names in a language without any kind of namespace support can only work for a certain type of applications and developmenent organization.

C really shines for low-level programming: while something like Linux Kernel is extremely complex, it has relatively simple needs as far as interface goes. The public interface is very small (defined by system calls - I don't know the count, but suffice to say the most trivial python libraries will often have more functions). Same thing can be said for other applications where C has been very successfull for.

But something as fundamental as a lack of any namespace in C really hurts when you need to handle very large applications which cannot be easily split in very well defined components with small interfaces. Complex GUI and browsers come to mind, which is exactly the kind of things where C++ is a well accepted choice.


> But something as fundamental as a lack of any namespace in C really hurts when you need to handle very large applications which cannot be easily split in very well defined components with small interfaces.

There is a degree of namespacing in C. Each compilation unit introduces own, anonymous (cannot be addressed/indicated directly) namespace. Symbols defined `static' (``having static linkage'', actually) reside only in it, and are not visible outside of that namespace. Symbols with external linkage are the API of the namespace. It may sound strange, but it's perfectly OK to have symbols with same name in several namespaces, as long as they are static (residing only in it); no conflict because they are not addressible from outside. In a way, a C's compilation unit is similar to C++'s class -- static (C) / private (C++) elements are visible only from this compilation unit (C) / class (C++).

So yes, you can easily split any C application into well defined components with small APIs, without risk of collision between names of private symbols. You only need to ensure unique naming among the `public' API symbols.

Linker use and linkage is a lost art those days, eh ;-(


I know very well about the use of static within compilation unit, but I was referring to "public API" (more exactly: API between components of your application).

This is especially excruciating once you need to use various libraries outside your control.


I also tripped over this argument in the first chapter, but it should be emphasized it's an exception of a bad argument in the book, not the rule. The author would have been wise to avoid this red herring.


I don't know for Java (luckily haven't been using it much) but for C I dislike typedefs. I need to know what data I'm working with. A typedef hides that information from me behind a shiny but uninformative name.


I mostly agree, except for function pointers. The C idiom of typedefing a function-pointer type results in code that looks more readable to me (e.g. prototypes of functions that take a function-pointer parameter have "typename varname" syntax) compared to the somewhat odd syntax you get if you scatter around function-pointer types inline.


It's nice to see that the things he describes as C's greatest virtues are the same things we carried over into the design of Go. (http://golang.org/)


It appears he has something to say about that, too:

http://www-cs-students.stanford.edu/~blynn/c2go/


I love the link to OTCC (the Obfuscated Tiny C Compiler, http://bellard.org/otcc/). It frankly blows my mind.


I was quite disappointed though that most of the obfuscation comes from the use of the preprocessor. I can make any program in any language look hard if I can rewrite it with an m4 macro before running it.


I don't think you'd need infinite monkeys to come up with that. It closely resembles what my dog produces when laying on my keyboard.


There are some reasonable things here, but:

"Lumping together function pointers that operate on the same data structure is essentially object-oriented programming" is unbelievably contentious regardless of which of the various definitions of OOP you follow.


I would strongly disagree that it's contentious. (Think that over...disagreement about contention. I raised my eyebrow too.)

Some of the more exotic features of what people call "OOP", like polymorphism, take a bit more care and feeding to pull off in straight C. They are most certainly possible, that being said.

Unwind "OOP" to "working with objects that contain data and methods together," and structs with function pointers fit the bill. I've had people stand in my face and practically shout that C++ is object-oriented and C isn't, and one refused to believe or admit that C++ basically compiles down to C in the end.


If you try to equate C and C++ by saying C++ can compile to C (not sure if that's what you're saying but it sort of looks like it), you might want to change your argument a bit. Haskell can also compile to C, but that doesn't make C strongly-typed, functional, or garbage collected.


At a high enough level of 'care and feeding' it's possible to pull off these things in assembly, or Fortran, or whatever, rendering the term completely meaningless.

C++ 'basically compiles down to C' for some constructs more than others (exceptions, RTTI, multiple inheritance goop) but if the C it compiles to is dark, arcane, unreadable crap, then this is no more illuminating than the truism that C++ basically compiles down to assembly language.


> C++ is object-oriented and C isn't

Neither is object-oriented, except C++ allows you to easily work with objects unlike C, where you have to reinvent them.

It is easy for me not to pick C if I need more abstraction than a struct with pointers to functions can provide.


"Neither is object-oriented"

And thus the discussion devolves into a pissing contest over whose programming language is the most object oriented...

Look, one can reasonable agree to disagree on the definition of 'object-oriented', but I don't see how you can seriously state that C++ is not object-oriented. According to the most broadly accepted superset of what constitutes 'object-oriented', C++ is object-oriented (or rather, can be used in such a way).


You're right, I meant that it's not strictly object-oriented, it supports multiple paradigms, with OOP being one of them. :)


Yes, that I agree with.


Yup, many "hip" folks believe they don't do OO just because they are using a language without classes, etc.

But what's the difference between foo->doshit() and doshit(&foo)? Those people still don't get stuff like data orientation, etc. but are shouting out loud how evil OO is and how they killed that dragon ;)


You're probably getting voted down for the tone, but I agree with your point. Case in point - GObject, from glib (foundation libs of Gnome, for those not familiar with it). I understand the historical reasons for that specific case, but is the macro magic really an improvement over using (a subset of) C++ ? I'm quite sure it's not.


GObject and glib in general aren't an improvement over anything, and both are largely shunned by the c-using community.


I'm deeply conflicted about this article because I agree with many of its premises. For instance: that C is superior to full-blown C++, that object-oriented programming is no panacea, that simplicity is good.

I have serious concerns, however, especially with the first page and the first few chapters.

- They support the biases of the myopic programmer who believes that now he or she knows C, they know everything one must know about programming. I know that isn't the entire point, but you can get that from the article and I have met such programmers. You don't want to work with them, even on projects written in C.

- The "C Craft" section largely describes hacks to work around shortcomings in the syntax or semantics of C.

- The languages used for "vs" comparison are: FORTRAN, C++, Java. Fish in a barrel, anyone? Haskell, APL & J are presented as curios. Python is only mentioned in passing, as an inferior choice to Haskell for rapid prototyping of mathematically-oriented code.

- Go is presented as the "better C", which is encouraging but I'd feel more encouraged if the author showed they were properly familiar with some additional modern programming languages and the cases in which one might use them.

- The assertion that "you can write object-oriented code in C" is accurate, although I think a better point to stress is "you can write mostly-well-modularised code in C, and that's what you want a lot of the time."

- The author also ignores the reality that object-oriented C really only scales up to a certain amount of object-orientatedness, and then it becomes very unwieldly if you are not very careful. Unwieldy at a scale where using a small subset of C++ (ie "C with classes") would remove the overhead, improve the code's signal-to-noise ratio, and still not bring in most of the "bad C++" that the author is talking about.

- The author seems to have chosen to define certain terms as they see fit. For instance, simplicity is defined in terms of brevity & terseness but the example used to prove the point is that Eiffel requires "character" and "integer" whereas C only requires "char" and "int".

For an alternative point of view on what constitutes "brevity" and "simplicity", see the common C idioms for filtering or mapping any variable sized data structure. The only time it becomes less brief is if you rewrite it in C++ w/ STL or Boost. ;)

- It's also telling that in Chapter 2 the Fibonacci counterpoint to Java is in Haskell, not C. That's because a full C program would look pretty similar to the Java program quoted, albeit without the sore-thumb of wrapping it all in a class .

Anyhow, I should quit ranting but IMHO (a) you should know C, (b) you should respect C but (c) you should know some other languages and use C only when you actually need to.

(c) may not apply if you're a super-whizz genius C programmer, some of those people seem like they can carry off ridiculous use cases without making horrible messes. Most of us are not those people. ;)


"that C is superior to full-blown C++[...]"

I disagree that C is superior to C++ for most projects.

First of all, C++ offers higher-level semantics which leads to an increase in productivity. According to [1], the ratio of C++ to C LOC is 1 to 1-2.5, but I'd be interested to read other studies or articles. My experience and [1] also contradicts your statement that rewriting code with STL/Boost will lead to increased code size. It's almost impossible for C++ to be more verbose than C - consider that you can write C-like code directly in C++.

Second of all, for a developer that has good knowledge of both C and C++, writing quality C is harder. C is very prone to programmer errors, much more so than C++ where you can choose to use libraries/language features that completely eliminate some of these errors. e.g: scope-based destruction, the STL data structures, string classes, etc.

Third of all, when discussing C++ many try to make C++ look as a frightening monster ("full-blown C++"?) where you have template meta-programming interacting with automatic conversions and operator overloading and all the other features in the worst possible way. This is not much different to claiming C is bad because you can use macros and void* for everything. The languages should be compared by real-world use patterns, not dreamed up criteria.

[1] Estimating Software Costs (Jones 1998) and Software Cost Estimation with Cocomo II (Boehm 2000)

"[...]that object-oriented programming is no panacea[...]"

While it is true that some have argued for OOP as panacea, this is mostly a strawman. OOP is one possible solution that works most of the time.


> First of all, C++ offers higher-level semantics which leads to an increase in productivity.

Strongly disagree. The full C++ spec, templates and all is pretty complicated. I am sure you can squeeze out less LOC out of it, but then why not just use APL.

> Second of all, for a developer that has good knowledge of both C and C++.

I think there are very few people who know C++ well. And by know I mean they know all of its aspects equally well. Now there are plenty of people who _think_ they know C++, but don't or just know just some parts of it well. I can easier trust someone who says they 'know C', because it is a fairly short spec, not so with C++.

If you imagine a team of developers. If all of them are true C++, experts, then everything is fine. But I have never seen that. So one developer likes templates, another likes some other part of C++, and they start using that and eventually they all write C++ but they can't all necessarily read and understand each others' code. And here is where another 50 pages of C++ style guide comes in to help. But if a language needs 50 pages of custrom style guides to be useful, there is probably something wrong with that language.

> It's almost impossible for C++ to be more verbose than C - consider that you can write C-like code directly in C++.

I think it is a false dichotomy. I know the original article compared both head to head, your point picked it up, but in general it should'be be between C and C++ only. It should be C++ vs C+Python or C vs Go and so on. In other words comparing lines of code by presenting a somewhat artificial constraint is not that representative of the real world.


Templates again... Have you used them? In my current project we've used data structure and algorithm templates from the QTL (similar to STL) instead of inventing our own buggy versions.

Then we've used templates to stream arbitrary data-types into an IPC socket.

Finally, we've also used templates to create a generic repository class and to create a specialized compare function for multiple types of containers.

All of these resulted in lesser lines of code without complicating the code base. This kind of template code is nothing fancy and brings real benefits. Now it's your turn to tell me how templates have complicated your projects. Please use real-world examples.

By the way, we're not all true C++ experts in the team and yes, some of us prefer different coding styles. You know what we did? At the beginning we created a coding guidelines/standards document that outlines what is acceptable to use in the project. Problem solved. What you describe - "can't all necessarily read and understand each others' code" - is a dysfunctional team and a management failure.

"it should'be be between C and C++ only"

Ok, we can compare Python + C++ with Python + C, but C++ wins there too. e.g: PyQt/PySide.


> Templates again... Have you used them?

Not much and I have nothing against templates. Just used them as an example of C++ features. It could have been any one of: RTTI, nested classes, smart pointers, the rules of inheritance and access control, friends, exceptions, operator overloading, streams and so on. All of this is well described but in about 1000 pages in Stroustrup's book.

> At the beginning we created a coding guidelines/standards document that outlines what is acceptable to use in the project. Problem solved.

Well that was actually one of my points. If you need a 50 page language style guide, then I would argue there is something wrong with the language. The whole K&R book is just a couple of hundred pages.

That is why I would believe someone who says "I know C" vs someone who says "I know C++".

I guess I just don't see the advantage of learning the "++" part of C++ vis-a-vis the resulting increase in readability/productivity.

> Ok, we can compare Python + C++ with Python + C, but C++ wins there too. e.g: PyQt/PySide.

Well, maybe. Most Python extensions are still written in C. Python itself has a native C API. There is the ctypes library. For every Python & C++ library integration there are hundreds of Python modules with their performance critical parts written in C. The reason PySide is so nice is because they were able to hide the C++ part very well behind Python. It is certainly a development success story, but what I had in mind was the process of implementing the prototype in Python, then finding the performance critical parts and re-writing them in C.


LOC means nothing, and if you want to generate subtle programmer errors, then templates is definitely the way to go.


LOC means a lot when it comes to:

* reading and trying to understand source code

* maintaining source code

* typing and programming-related injuries

The dynamic-typed camp argues that even declaring a variable is a pain. Conciseness is one of the favourite features of JS/Ruby/Python developers and prolixity is universaly loathed in Java.

Please expand on your assertion that subtle programming errors are caused by templates. I'm interested especially in errors as a consequence of using templates from the STL or to build generic code. i.e: not metaprogramming


> reading and trying to understand source code ... maintaining source code

I think it helps but in general those things are not related very well. For examples guess what this line of code does (this is APL) ?

    X[⍋X+.≠' ';]
According to APL's Wikipedia entry it "sorts a word list stored in matrix X according to word length". It is just 1 line of code so it wins there. But now imagine reading code like that and maintaining it.


It's silly to expect to be able to understand text in a foreign language. If you want to read APL, learn APL; It's a great language.


An extreme example doesn't prove much and there are many languages that successfully balance readability with LOC. e.g: Python, JS.


Right, I was just trying to highlight that a simple metric of LOCs is not enough. One can right cryptic code to minimize LOCs but that usually leads in harder to read and maintain code.


"if you want to generate subtle programmer errors, then templates is definitely the way to go."

Please indicate how so. I cannot think of ways that make templates more prone to programmer error than, let's say, str_xxx functions for which the programmer needs to allocate extra room for a \0 (or not, depending on which function you use...). Those types of C idiosyncrasies cause much more mistakes than any inadvertent template overload ever could.


agreed on str...(), but this is not semantic, is broken lib that is hard to update for backward compatibility issues, so in some way it is not a matter of the language.


Fair enough, but I'm still interested in examples of subtle programmer errors with templates.


Judging by his blog (http://benlynn.blogspot.com/) it looks like he has considerable affection for many languages beyond C. And I think you may have misinterpreted his comparison with Fortran: rather than showing how handily C beats Fortran (fish in a barrel), he seems to be saying that even with some C99 improvements, there still are places where Fortran has the advantage (refreshingly heretical). It's definitely not a piece for beginners, but I think it may be a more balanced picture than you present.


Yeah, you're right about Fortran, my mistake. I actually skipped that chapter because I've never written Fortran. I did read the other chapters, paying particular attention to languages I've used at least a bit (Haskell, Java, C++ from that list.)

Like I said also, I agree with a lot of the premises. I just think the essay is overly positioned as "C is the best hammer" when a better essay might be "Use the right tool, C is a powerful tool. Here is how to use C like a craftsman, here are some good ways to use C you might not have thought of."

It's definitely not a piece for beginners

Who is it for? (I mean this in the most respectful way possible, I'm actually not sure.)


That's certainly a fair question. I think he probably wrote it mostly for himself: a CS graduate who fell hard for some modern languages and then later realized that neither OO or functional has all the answers. Like someone raised in a strict religious household who's always had his doubts about the faith, he finally feels confident in expressing his true feelings, and maybe gets a little carried away. I like it though.


Also, I didn't mean to suggest the author is necessarily a myopic programmer who believes that now he or she knows C... - just that the way this essay is positioned, I see it as appealing strongly but superficially to such programmers.


>that C is superior to full-blown C++

Couldn't disagree more. C++ has a much better type system than C. For many C++ programmers (myself among them) C++ was less about OO and more about giving C programmers the ability to create new types that have the powers of the built in types. I can have strings with proper string schematics instead of an array of chars that is null terminated by convention (i.e. can't be type enforced).


That's indeed a great feature of C++. And there are more great features (auto-destructors/RAII, better "const", parameterized types).

But I find other features/interactions in C++ are unnecessary and even harmful (inheritance and virtuals, typedef of references, C++'s useless implementation of checked exceptions). Templates are implemented pretty badly (Haskell's type-classes a.k.a the discarded "Concepts" could have been used to reduce the auto-generated code duplication). The "rule of three" (Copy constructors, assignment, destructors) is not enforced by the compiler and is a great error-prone hole.

C++ exceptions are problematic, and I actually prefer C's lack of mechanism, where it is completely clear (as long as nobody uses "longjmp") what the control flow is, from simple static analysis or code review. In C++, you have to keep all of the code exception-safe, but due to interaction with external libraries, it can be tough and even error-prone. Cleanup order is sometimes very important and also implicit when using exceptions, making review of its correctness much harder.


Good points. I would say that it's better to have too much, so long as I can ignore the bad parts (e.g. checked exceptions) than not enough as in C.

I also disagree about exceptions. It does make control flow trickier to follow, but the problem with C is that to really have all failure cases covered you have to put every single function in an if statement. Anything and everything can fail.


> Anything and everything can fail.

In my experience this is only true until you switch to a style where memory allocations (which are used in "anything and everything") are given to functions as arguments (from the top) rather than via malloc or such.

When allocations are given as arguments, the main reason for errors/failures disappears and a blissful ripple effect of failure-returning -> void-returning functions usually results.


It's probably worth reading the Preface.


Solution: C#


Desert island language, as in "like trying to build a hut, and manage your hunting and gathering with the Swiss Army knife that happened to be in your pocket"?

I agree that for Torvalds work it is the only sane choice. But I'm not writing kernels.


It's along the lines of "if you were stuck on a desert island and could only have one whatever, which would it be?"

So if you had to choose one programming language to do all of your work in, which language would it be?


Actually, I understood. I used to listen to Desert Island Disks 15 or 20 years ago--a local NPR affiliate's version.

One programming language? As I recall DI Disks let one pick 10 pieces of music, and threw in the Bible, all of Shakespeare, and a luxury of your choosing. But I suppose that if one were not distracted by King James, King Lear, or a luxury, the sensible thing would be to follow in steps of Wall, Ousterhout, Van Rossum, and Matz: use C and create an extensible scripting language.

(edit: spelling)


I didn't get the reference either. It fits my ego better to think of it as the Desert Eagle[1] language, I think. Or at least my subconscious seems to think so, I just misread the title of this story as such.

[1] http://en.wikipedia.org/wiki/IMI_Desert_Eagle


Another excellent resource (a Git tutorial) by the same author: http://www-cs-students.stanford.edu/~blynn/gitmagic/


You can easily beat "static const volatile signed long long int bar" with a function pointer (and without the unrealistic redundant indirection of "int const ... const foo"). Start with static const volatile signed long long int (*foo)(static const volatile signed long long int bar). :)


calls by function pointers can't be resolved at compile time and thus won't be optimized. depending on the program you're working on you might want to consider this.


Not strictly true, it is quite possible to statically resolve many uses of function pointers.


"Other object-oriented programming preachers rebelled in practice, by spending most of their time in “fun” languages like Perl or Python. After all, who enjoys the boilerplate and verbosity of Java?"

- What has the verbosity of java vs python have to do with the validity of object oriented programming?

"New languages like Erlang and Go have become popular despite not being object-oriented. In fact, Joe Armstrong, inventor of Erlang, explains why OO sucks."

- Actually later Joe Armstrong came back and said the following:

"Actually it’s a kind of 180 degree turn because I wrote a blog article that said "Why object-oriented programming is silly" or "Why it sucks". I wrote that years ago and I sort of believed that for years. Then, my thesis supervisor, Seif Haridi, stopped me one day and he said "You’re wrong! Erlang is object oriented!" and I said "No, it’s not!" and he said "Yes, it is! It’s more object-oriented than any other programming language." And I thought "Why is he saying that?" He said "What’s object oriented?" Well, we have to have encapsulation, so Erlang has got strong isolation or it tries to have strong isolation, tries to isolate computations and to me that’s the most important thing. If we’re not isolated, I can write a crack program and your program can crash my program, so it doesn’t matter.

You have to protect people from each other. You need strong isolation and you need polymorphism, you need polymorphic messages because otherwise you can’t program. Everybody’s got to have a "print myself" method or something like that. That makes programming easy. The rest, the classes and the methods, just how you organize your program, that’s abstract data type and things. In case that the big thing about object-oriented programming is the messaging, it’s not about the objects, it’s not about the classes and he said "Unfortunately people picked up on the minor things, which is the objects and classes and made a religion of it and they forgot all about the messaging."


It says:

"In my Eiffel days, I was encouraged to write "integer", not "int", "character", not "char", and so on. I believe Java encourages this practice too."

1. No Java uses char, int, and so on.

2. No, C is absolutely not concise even compared to Java. At least how I write Java code.

Look at this Java method:

String strangeConcat(String str1, String str2) { return str1.substring(0, str1.length() - 1) + str2.substring(1, str2.length()); }

Is it really more concise in C? How do you tell the caller to free the memory of the returned string?

And if you compare C to Ruby, Python, Scala, Clojure etc... then it is no question that C is not a concise language for most tasks.


In Java int and char are primitive types. They don't provide methods so:

  int i = 4;
  i.toString(); //Exception - int isn't a class, so i isn't an object, so you can't call its methods.

  Integer j = 4;
  j.toString(); //Returns "4" as a string, because Integer is a class.
Java would 'encourage' the latter practice at times because its useful to be able to call methods off of characters and integers and such, but there is a difference.


But you wouldn't do this in Java (need a random string for an integer).

Instead you might see:

  logger.fatal( String.format("Program crashed on index %d: %s", i, message) );
I think C has a similar construct, not quite as verbose, that would save 7 characters out of the line...


There are other methods on Integer and such - it was a contrived example because I was too lazy to read the docs.


Just the notion of a single desert island language is a bit humorous, but if I had to choose I'd pick something meatier like Haskell; and not out of lack of fondness for C.


I'm assuming the island has power and modern computers.


The "Power" section made me think of Blub. http://www.paulgraham.com/avg.html


Good thing we don't work on deserted islands right?


> Not only is C easy for humans to understand,

Is he being funny or serious?


Serious. C is a simple, elegant language. If your natural inclination is towards low level tools without elaborate abstractions built in, then yes it is easy to understand. Easier than the equivalent assembly language for example.


Assembly language is generally easy, excepting only that it typically doesn't insulate issues of memory addressing, &c. The big advantage of C over assembler is portability.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: