The Go Programming Language, or: Why all C-like languages except one suck.
syntax-k.de
syntax-k.de
The problem is that when you notice something inaccurate in a document, you have the tendency to ignore the rest...
It is a superior alternative for classic application development, and some nice slim and full-featured GUI toolkits use its qualities well.
I would say C++ is the way to go for servers, not really GUI. As much as I love C++ I wouldn't recommend using it for writing a GUI.
dynamic_cast<MyData>(funky_iterator<MyData &const>(foo::iterator_type<MyData>(obj))
I get it's a joke, but it would be a better joke if it was actually valid C++ or close to something you would actually write.
contemporary C++ using STL looks like a classic case of the "If all you have is a hammer"-syndrome
I don't understand what it means. The STL is a very powerful tool to implement complex data processing and work on structure. Is this another case of someone using the containers without using the algorithm's functions?
First of all, like you've mentioned, his C++ example code is bogus. Second of all he says "modern funkiness of dynamic languages" only to continue with lambda, map/reduce and type independence. The first two are unrelated to dynamic/static typing, the latter is incorrect or badly phrased. You don't get "type independence" in dynamic typed languages, the types are still there. Thirdly, he greatly exagerates the complexity of templates (thousands of incorrect syntax options for smart pointers, all you have is a hammer, etc)
It's a classic case of fishing for arguments to support an idea.
for(vector<some_really_complex_type>::iterator itr = something.begin(); itr != something.end(); ++itr)
Then you need to use "itr." It's a pointer-like thing that isn't quite a pointer, and when you hold pointers in your container, which you often (usually) want to do you have to deal with a pointer-to-a-pointer which is almost never pleasant. And when you deal with keyed containers you have to remember the iterator actually points to a pair so you have to do .first and .second (why not .key and .value?)Braindamage doesn't even begin to describe it.
The containers have been designed with this is mind.
If you don't use the algorithms, indeed, the STL can be cumbersome to use.
See std::for_each, std::transform, std::accumulate, std::partition, etc., etc.
It is available in compilers today -- for example, Visual C++ 2010 supports "auto".
What about the linux kernel? Or GCC? Both projects are on the order of millions of lines of code. The author's claim is simply not true.
And of course you are going to write an operating system in C.
The problem here, and the massive, massive thing keeping me from throwing my full recommendation behind it, is that D fails entirely on #7, because the community is small and so even installing libraries by hand can be tedious. I keep wanting to pull out D for personal projects, but then I come across some obscure, poorly-documented library with few/no alternatives, and after trying to build it for three hours, I give up and switch to something else. Recently, 'something else' has in fact been Go. I still feel like, in an ideal universe, I'd rather program in D than Go, but we do not live in an ideal universe, and of those two, Go is the practical choice. (And, despite my frustrations with Go, it is still better by leaps and bounds than Java and C++.)
Also, quick correction: any dynamic language worth its salt does the same short-circut evaluation with and and or, including Python, Ruby, Scheme, and Common Lisp, so they all have the property ascribed in this writeup to only JS and Perl. In Python, you can change whether instances of a class are 'true' or 'false' values by overloading the __nonzero__ method, which means e.g. empty user-defined data structures could be considered 'false' while non-empty ones could be 'true.' On the other hand, Ruby considers only false and nil to be false values, Scheme considers only #f to be a false value, and Common Lisp considers only nil to be a false value. Aside from individual quibbles about which values are true and false, all of these languages implement an or that returns the first true value it finds, and all of them implement an and that returns the first false value it finds.
EDIT: Lua also allows the short-circuit boolean operators to return values. The only widely-known dynamic language off the top of my head that doesn't do this is Smalltalk. This would be complicated to add to a type system, for relatively little gain, so as far as I know, no typed language allows it.
An aside - using implicit typing in c# you can have an object perform as a boolean in boolean expressions. You can also use implicit typing for more than just booleans as well.
config_file = get_config_file() or "~/.my_config"
(which is valid Python; if get_config_file() returns None or False or an empty string, then it sets config_file to a default value instead) because the typing problems get nasty. (You'd have to stipulate that 1. boolean expressions can return non-booleans and that 2. all the arguments to a boolean expression must be of the same type, and, if you want to include a not operator, 3. there are 'canonical' true and false values for every type, so you can evaluate expressions String s1 = !"some_string";
String s2 = !"";
which is why the typing rules would get... uhh, complicated.) config_file = get_config_file() ?? "~/.my_config";Wouldn't the 'not' function signified by your operator just return a basic boolean (for any argument which was a subtype of boolean, which would be any argument...)?
[To be clear, "go read 'Book That Will School You' " is an acceptable answer to me, if it really will]
It is a great book, but a little dense. Its example language is a typed lambda calculus. This is both good and bad. The good: you can evaluate most expressions in your head or with a pencil. The bad: sometimes it is difficult to read without evaluating the expressions.
It is heavy on proofs and mathematics, but it does what it says: it introduces you to the basics of type theory as applied to programming languages.
Edit: an amazon reviewer pointed at http://www.amazon.com/Foundations-Object-Oriented-Languages-... as an alternative book. I haven't read it but it does look useful.
Instead of using a Boolean, use a type that means what you actually want: You want to short-circuit combine two (or more) values, returning the first one that is valid, without evaluating the later ones.
You need a type that represents a possibly-unavailable value. "Boolean" is not that type. "Maybe" is Haskell's type-safe "Nullable" type. It has two kinds of values: "Nothing" or "Just a", where "a" is a value of any type.
Some quick simplified definitions of Haskell terms: "Control.Monad" is Haskell's generalization of "flow control" "mplus" is Haskell's generalization of "or" (as it means in Perl/Python).
backticks are used to make a regular prefix function into an infix operator.
"undefined" is like Perl's "die" or Java's uncatchable Exception, used here to show where short-circuit happens.
ghci is a Haskell interpreter.
% ghci
> import Maybe
> import Control.Monad
> undefined `mplus` Just 1
*** Exception: Prelude.undefined
> Just 1 `mplus` undefined
Just 1
> Nothing `mplus` (Just 1) `mplus` undefined
Just 1
> Nothing `mplus` (Just 1) `mplus` (Just 2)
Just 2
This web page goes into a bit more detail on this technique:
http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htmIt's slightly complex to understand, since it so generalized, but in practice it makes for simple, safe code.
x || (y && z)
would default to being the 'most general' type of all its arguments, which could be Bool but might be more specific in certain circumstances. But this of course only works if your language has some kind of subtype relation, which is not necessarily true of every language—it is not, for example, true of Go, or most typed functional languages that I know of—and it still ends up having typing rules like t1 : A t2 : B A <: C B <: C
------------------------------------
t1 || t2 : C
It also assumes that the Boolean class is implemented as a single class with no subtypes for True and False—i.e. not like Ruby's TrueClass and FalseClass; you'd have an instance variable or something which tells you whether an instance is True or False—because if you implemented it with singleton instances of a True class and a False class, then you'd bifurcate your whole object hierarchy, and it also assumes that there can be no type 'more general' than Booleans, because if there is something 'above' Boolean in the hierarchy, then you'd have to rewrite the rule as t1 : A t2 : B A <: C B <: C C <: Bool
------------------------------------------------
t1 || t2 : C
and... well, it does get a little complicated.This gets particularly interesting with mathematical code. If you have a templatized math function like a linear interpolation function, you can swap in integers, reals or complex numbers without writing new code, and also matrices, vectors or quaternions from another library, provided that they have the algebraic properties the function expects. Go is nowhere near allowing this degree of write-the-algorithm-only-once, as it both lacks generic types and has numeric types as privileged constructs you can't substitute with user-defined ones.
We've managed to reach a very high leverage in std.algorithm (http://d-programming-language.org/phobos/std_algorithm.html) because of that, and there's seldom a need to redo by hand one of its algorithms for efficiency or convenience reasons.
http://live.gnome.org/Vala/LuaSample
Granted, Vala is what it is -- a language built for GLib -- but I think we've had enough languages try to take over the world. It's not unimpressive for a language just five years old and almost entirely community-developed to have a lot already being writen in it:
http://live.gnome.org/Vala/Documentation#Projects_Developed_...
including Ubuntu's new user interface. Since it only really depends on GLib and gtk, it's cross-platform enough for most purposes, though the majority of projects written thusfar in Vala have only targeted X-based desktops.
There's also pragmas for adding the needed library to the compile command line.
It's actually pretty easy to use.
D's C++ binding support, on the other hand, leaves quite a bit to be desired... I always do extern C functions to bind them together.
I'd have to say that Ken Thompson was directly involved with C, not just indirectly!
I was really just quibbling over definitions and connotations, when I hear of an "indirect" involvement I think of something very different and much more remote than the deeply intertwined stories of unix and C and Thompson and Ritchie at Bell Labs in the 69-74 era.
But Java? and Javascript? They both have C-style syntax, but apart from that they are both very different from C (and from each other).
Please don't say `C-like' when mere `C-style syntax' is meant. (And please don't think that having similar syntax implies any other close similarity between languages.)
I have no doubts that Go authors think that their syntax is superior, but they'll have a hard time convincing me that
switch nr, er := f.Read(buf[:]); true {
is understandable (snippet taken from Go tutorial)."Since the switch value is just true, we could leave it off—as is also the situation in a for statement, a missing value means true. In fact, such a switch is a form of if-else chain. While we're here, it should be mentioned that in switch statements each case has an implicit break."
The basic outcome is that:
1. The assignment to er, nr is an initialization statement for the switch.
2. The true (default value if not specified) is used to configure the switch as an if-else chain which is required as the assignment above makes the purpose of the switch ambiguous (is the result of the assignment configuring the switch - how do you do that as multiple values are returned?).
You could rewrite it:
nr, er := f.Read(buf[:]);
switch true {
...
}
or even: nr, er := f.Read(buf[:]);
switch {
...
}
But the switch initializer scopes it to the switch block cleanly. switch 3.141592654 { ... }
affect the case-statements inside? const P = 3.141
switch 3.141 {
case P:
fmt.Println("This prints")
}
When no value is specified after "switch", the value of "true" is implied, which is why you can do: switch {
case a && b: ..
case something():
}http://upload.wikimedia.org/wikipedia/commons/1/17/GObject_e...
I have no problem with C at all (I prefer it to C++).
http://shootout.alioth.debian.org/u32q/benchmark.php?test=al...
http://shootout.alioth.debian.org/u64/benchmark.php?test=all...
These are the x64 quad-core measurements -
http://shootout.alioth.debian.org/u64q/compare.php?lang=go
> given that this days most systems are x64
Given x86 and x64 and single-core and quad-core are all out there - the benchmarks game shows measurements for each of them.
C# only covers a very small subset. Somewhat more if you include Mono, but I guess that puts it in the same league as Java which he did mention.
func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request)
and at first I have an actual hard time parsing it, and then I think, why can't this just be Draft75Handler.ServeHTTP(writer, request)
I partially regret this loss and partially rejoice in it. I'm sure it'd just take a bit of practice to pick it up again.Edit: I know why it can't look like that (because it can't be dynamic), but its still what crosses my mind.
How do you differentiate your simplified function declaration to a function invocation?
The latter means you must refer to the receiver (the instance of Draft75Handler) as "this" or "self". In Go, you name it explicitly (in this case "f") which makes the code more readable IMO (although "f" is a strange choice in this case).
The variable names "writer" and "request" will become wearying as you use them in the function body - better to say the exact type once (http.ResponseWriter) and use shorthand thereafter (w).
(And, obviously, omitting the type information in the function arguments doesn't work in a statically typed language.)
It works just fine in Haskell for most cases.
Rust supports RAII, but it might be premature to include Rust in this kind of comparison.
The author did mention that GC has been around for C and C++ for ages yet people don't seem to use it. If manual memory allocation was such a big problem for C++ programmers people would have adopted a GC library long ago.
Which is not to say Go might not gain traction for other reasons but it's not really clear to me what problem it solves, even after reading the otherwise very entertaining and informative article.
Python does that as well:
0 or False or 'Python rocks' or [] == 'Python rocks'I find it ironic that this "feature" is #1 in the list.
It makes me wonder, why is concurrency really that much of a black art in 2011? I still see people confuse parallelism and concurrency and just the other day an article got upvoted here describing why JavaScript programmers don't need to learn about concurrency; as if the continuation-passing callback style of JavaScript isn't a concurrency technique.
You can also squeeze java a lot, running in less then 16MB. So have I miss the point, or the writer is a GO-addicted?