The Go Programming Language, or: Why all C-like languages except one suck.
syntax-k.de
syntax-k.de
IMHO one of the few things that should be addressed at language level is that structures with function pointer members should have a way to be called with the implicit structure pointer (something like "this") as argument. That's all we need from OOP for C. This makes you able to do:
list *mylist = createList();
mylist->push(foo);
mylist->pop();
That's what currently you do like: mylist->ops->push(mylist,foo);The biggest problems I've had with large C codebases are deep assumptions about data structures, spread throughout. The biggest benefits of OOP, IMO, come from firewalling off internal details from client code through the use of an interface; knowing that client code has to call you, rather than simply follow your internal links, gives you a lot of freedom to change things after the fact. A polymorphic reference that the client can't dereference and poke about with gives you that.
(Specific support for any single downcast is trivial - just write a function for the function table, typed appropriately and implemented appropriately.)
As for an implicit "this" for function pointers, I've had the same wish myself. I've settled for just a function namespace (ie: myListPush(myList,foo);) due to the verbosity of your example but it's not quite the same. At this point I'm pretty happy without it, and even think it might be hiding too much information, but it makes a nice piece of syntactic sugar for sure. My current unneeded-but-desired change to C would be the addition of lambdas like in C++11.
I've been meaning to try Go for a while (the fast compile times alone intrigued me), but this type of post really just puts me off - if you're telling me that C isn't suitable for large projects, then that tells me you may not know C well enough to make that judgement.
The only person who knows C better than the lead designer of go. (Ken Thompson IIRC.) Is in a casket right now.
EDIT: I didn't mean that in a ill-spirited tone. I'm just pointing out that the people working on go are the best alive.
But then; who is?
If there's evidence to support that there's someone who knows it better than Ken, I personally would like to see it. (More from curiosity than anything else.)
Frankly, even if Unix was that project, I'd expect that the "best" C programmer may actually be someone who is in a much visible position.
Then again, Ken may very well be the best C programmer, but Go doesn't much seem like a language designed to address the issues that C is currently being used to address. Frankly, it seems like a much better Java replacement IMO.
The Xen hypervisor (which shares a lot of characteristics with an OS kernel) is written in OCaml, which seems to have proven a good choice in practice: http://gazagnaire.org/pub/SSGM10.pdf
> projects with more than 10k LOC.
Other comments have discussed this. Linux kernel: ~4million lines. Compilers, interpreters, and so on... all well above 10k LOC. While C is not the best language for such things, by no means is it unmanageable.
> But when you try to do the modern funkiness of dynamic languages (lambda functions, map/reduce, type-independence, ...)
All of the features can be used in much stronger type systems than C++ gives you. ML-family languages have all these features and are strictly type.d
> Javascript Javascript's whacky semantics suck, that's for sure, but for a dynamic language that's par-for-course anyway. I don't really understand his argument about embedding. Why is that relevant? Java and C# are not embeddable either. ...So?
I believe John Carmack would have something to say about this.
And GIMP?
The author might be talking about consumer-oriented software, but still saying that C isn't suitable for large codebases is very presumptuous.
disingenuous was the wrong word
Like for instance, C compilers. Cripes that 10k figure is so absurd...
I would say SQL or HTTP server or game or a word processor where you have to retain sizable chunks of data in memory for longer periods is a better example of big C project. And that's where lack of automatic memory management and data abstraction in C makes it hard.
My point is, even having one Carmack level developer on a team is a bridge to far for most.
Yes, you can write huge projects using just C, but you honestly don't think most people would recommend it unless every little drop of performance was really that important or you had no other choice or you had a super guru with an amazing track record writing the software.
At the time Q3 was written (13 years ago), console games were still being written in assembly language.
But yes, N64 games were written in assembler.
MAKE QUAKE-C
Though for Doom3 and on, they did switch to C++, which makes absolutely minimal usage of the language features, heh. I guess old habits die hard.This is true of almost every C++ project I've ever worked on. Unfortunately they all used a slightly different minimal set of language features...
However, I did eventually spot Julia, which is totally awesome for my own personal higher level needs.
I don't know what FLINT is or what Julia is used for (Wikipedia makes it sound like a functional language?) so Lua might not be the best fit, but it's designed from the ground up to mesh with C (to the point where the solution to many otherwise simple things is "write that bit in C")
One might think C sucks and still use it. You might think it sucks, but it's still less bad than everything else. Using something doesn't necessarily makes you blind towards its drawbacks. I think a lot of the things I use everyday sucks. But I still use them because there's nothing better... yet.
All those programs written in C that everyone is posting about (quake, linux, gimp etc). Were written before Go existed. So it's very plausible that the authors could agree that C sucks if they had the option to write in Go. Which is the point of the article.
Microsoft has standardized C# and the CLR through ECMA, and issued a promise stating that they will not assert their patents against alternative implementations thereof. Non-standardized APIs like ADO.NET, ASP.NET, and System.Windows.Forms are not protected by that promise, but Mono discourages their use anyway and if they had to remove them, it would not affect the C# compiler or CLR in any way. So this is not a legitimate reason to avoid using Mono, unless you (a) need to use the non-standardized Microsoft APIs or (b) are Richard Stallman.
However, some commercial development shops won't touch anything LGPL (a license compatible with commercial use). Mono adds additional complexity. The concerns of the FSF are here: http://www.fsf.org/news/2009-07-mscp-mono
The Mono project's perspective can be found here: http://www.mono-project.com/FAQ:_Licensing#Patents
Guess what? Those same points apply to an language. Heck the evolution of a Python programmer https://gist.github.com/289467 currently on the front page which does the same thing for Python which is usually pretty clean. Even PHP which is mentioned as the alternative can quickly become a spaghetti ball mess of imported code and shared snippets.
I don't love Java, but for certain classes of problems it is an excellent choice and to discount it based on a wrong perception just shows how shallow a technologist you are.
Potentially, yeah, but guess what? It does not happen in every language. That's why it's most often brought up for Java. Because it's not just the potentiality that matters, but the actuality too. In other words, it also takes a culture, and Java very much has that kind of culture.
Heck the evolution of a Python programmer https://gist.github.com/289467 currently on the front page which does the same thing for Python which is usually pretty clean
See? Usually, as in, "Usually pretty clean", is the key word here. Whereas Java program design is usually pretty convoluted.
And the "Evolution of a Python programmer" is mostly a joke, meant to show the tendency of some Python types to use some newer/cooler/functionaler (sic) features in place of more simple and readable ones. It's not what commonly happens in Python projects though.
Though you seem to be concerned about such kind of material being posted on HN. At first I was inclined to agree that it doesn't belong here. Yet consider, there's probably a lot of opinionated articles that include a fair bit of useful information (knowing others' opinion might be useful by itself), while bias can be neutralized by critical thinking, which HN audience hopefully is capable of.
And I'd argue that on HN relatively many articles get to the front page even without sensational headlines.
Positive first, negative second, or better yet, you understand the negative argument through the positive.
I'm not a fan of the verbosity of error handling with exceptions either. It is particularly bad for fine grained error handling.
However, I do like that the default state of a non-handled error is to propagate and eventually crash the thing rather than continue with errors that may subtly corrupt things. I haven't used Go, but from the explanation of error handling Go seems to do the latter rather than the former (please correct me if I'm wrong).
In most languages, exception handling doesn't seem designed for fine grained error handling, which makes one not want to use it for that. Rather than complain about the verbosity of their methods, why not try to fix the issue with a less verbose method?
E.g. something like: handle := openSomeFile() catch err
Or ignore it if you want, letting it automatically propagate: handle := openSomeFile()
Most errors are handled quite like the way you propose, e.g. handle, err := Open(). You can ignore the error if you wish, but that's up to you, and it's explicit that you're ignoring it: handle, _ := Open().
Panics are not meant to be a way to control the flow of the program, but rather to regain control of it if something disastrous occurs.
result, err := somePotentiallyFailingFunction()
if err != nil {
// Try to recover here
...
// Not recoverable?
panic()
}
And this call to panic works the way you prefer. Note that doing the following: result := somePotentiallyFailingFunction()
won't compile since the number of values on the left doesn't match the right, so if a function can fail and returns an error, you will know.
If you do something like: result, err := somePotentiallyFailingFunction()
And then never check err, it's actually a compile-time error, which enforces error checking, to a point. What's next is the kind of thing you don't like, and it's poor style. result, _ := somePotentiallyFailingFunction()
This assigns the error to _, which causes no errors if you don't use it.Hope this clears some things up.
I think too many people get wrong idea about exceptions from defaults in many environments. Exceptions being thrown should not normally be a sign of a bug; knowing the line number the exception thrown should be irrelevant information almost all the time, save for errors like access violations or null pointer exceptions.
Instead, the information contained in an exception can usually be turned into actionable data to a user or administrator of an app (depending on whether it's on the client or server).
type ParseError struct {
line int
}
func (e ParseError) Error() string {
return "Parse error at line: " + fmt.Sprint(e.line)
}
Note: The current release may vary slightly on the details. I'm using a pre-release build. But older versions are similar. switch err.(type) {
case db.IOError:
// Database exception here
case io.FileNotFoundError:
// File exception
default:
// Pass it to our caller for them to handle.
return nil, err
}
Which gives you the ability to selectively catch errors based on type.So, he chooses a language that is there because of Google's benevolent permission.
http://golang.org/LICENSE http://code.google.com/p/go/source/browse/PATENTS
This may all be a function of the environment in which my code runs, but I certainly have no interest in going back to my C days when 95% of the source was devoted error handling.
http://golang.org/doc/go_spec.html#Handling_panics
Edit: Also, here's an interesting pattern to illustrate that Go's panics are a little different.
func parse(s string, config *Config) (x Thing, err error) {
defer config.RecoveryPlan(&x, &err)
x, err = doParse(s)
}
It's contrived, but you can see that the "RecoveryPlan" can choose to ignore panics or recover from them and it can also affect the return value of the "parse" call by assigning to its parameters and recovering from the panic."The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values"
http://blog.golang.org/2010/08/defer-panic-and-recover.html
But crossing interfaces between modules is, IMO, core to exceptions. They're ideally suited to communicating the underlying cause of failure from the depths of the system to the top-most loop (usually a request/response dispatcher or UI event loop), where the error message can be logged or shown to the user, as required, indicating the nature of the failure.
I expounded further on this point a few years ago, related to Java's misadventure with checked exceptions, but also relevant to module crossing:
http://cafe.elharo.com/programming/bruce-eckel-is-wrong/#com...
I have been programming (in C) for 12 years, and when I see something like this I still get a shivering feeling. How can one design such a shitty programming language like this?
I suspect it's "only" x2 longer. And about the 100x faster -- you might want to re-evaluate dynamic languages if that was the case last time you tried.
LuaJIT2 loses to C++ in most comparisons, but only by 20% or so, while being much more dynamic than Java.
V8 is about x3 slower than C++ in real life benchmarks. PyPy in my experience is about x5, Python x10. That's significant slowdown, but it is a far cry from x100, and they are a thousand times easier to develop in than C++.
My weapons of choice: Python when it doesn't need to be very fast, Cython when you need to speed that Python up, C [not ++] when you really want it to run quickly and have full control. And K when you want to have fun.
[1] http://code.google.com/p/vitess/
[2] https://groups.google.com/forum/?fromgroups#!topic/golang-nu...
> What's even better: you can put labels on your nested loops and break out of multiple levels using them. Finally!
This is possible in other languages, too, even if it's a little known fact.
For example in java :
ext: for (int i=0; i<4; i++) {
System.out.println("ext "+i);
for (int j=0; j<4; j++) {
System.out.println(" in "+j);
if (j==2) break ext;
}
}At this point the article lost all credibility with me.
Unfortunately some of the implementation details do allow for seriously horrible code and some seriously horrible traps. But if you can clear the fog, and use "the good parts", it's actually pretty nice.
As for the article, I think it's very well written and seems to have a good overview of the language. Also, the author seems to have an above average experience level in an above average number of programming languages (guessing here), so it might be worth a read.
Since you aren't providing your sure-win idea changes, I can't be sure what you don't like but I can guess. All of them are well documented at this point. And they are easy to understand and avoid.
i'd love to see this
...nothing? ok here: http://en.wikipedia.org/wiki/Rust_language
What? Google's Issue 9 is neither a C-like language nor a systems language.