In praise of Go or "Why I moved from Python and C++ to Go"
groups.google.com
groups.google.com
Go seems to fall into a very small niche. The inclusion of a GC means it is not appropriate for a lot tasks that C/C++ are suited for. Also, it is verbose enough where you can't bang out stuff as fast as in Python/Ruby.
It's designed as a server-oriented (i.e. aimed at a still growing programming category) language that is close in syntax to C/Java (i.e. already familiar to many), almost as easy to write in as Python/Ruby (both very successful, non-niche languages) but order of magnitude faster and much memory efficient that either.
As a bonus it has much better concurrency support.
It's designed for the same "niche" that Python/Ruby/Java serves on the server, with many important improvements over them.
Having written code in Python and Go, Go seems to me an improvement over Python in many important areas (speed, memory efficiency, concurrency) and the parts that are not as good are both not as important and not significantly worse (static vs. dynamic typing is a toss (it's nice not to declare types but it's also nice if compiler tells you about a type mismatch typo that Python will only complain about at runtime), Python still has slightly cleaner syntax etc.).
I didn't experience Go being more verbose than Python to a degree that it mattered. In some aspects the syntax is actually less verbose (Python class require more typing than Go interfaces).
Go is still a young language and young implementation. On one hand it means it's anyone's guess whether it'll become non-niche at some point but at the same time I'm pretty sure at year one both Python and Ruby were much less polished and much less popular than Go is at the same stage.
Personally, I'm bullish on Go and if I were writing server side code, I would use Go (even though at the moment I know Python better).
Not substantially. It made the concurrency decisions for you ahead of time, and if you need a different concurrency model, you're up shit creek.
> but order of magnitude faster and much memory efficient that Python or Ruby.
So is my Radio Flyer Wagon. You still can't do systems programming, embedded, high performance, or real-time work with it.
I know too much about C and the constraints it works well in to believe Go is anything but awkwardly crammed between two realms of programming.
A language that forces its own GC upon you (note that you can have GC in C/C++, you merely have to choose one), and its own concurrency model upon you without giving you at least a couple choices is not a well designed language.
Even Clojure gives you a couple ways to approach concurrency, and it doesn't even make the same claims as Go has.
I don't know a single person who's done substantial systems programming who takes Go seriously in terms of their field.
Not a single one.
It's not true. I'm not sure what concurrency models you feel you need but let's go through the most popular ones. There's event based programming which is essentially single-threaded and predicated on structuring your code around poll/kqueue/whatever loop. You can do that in Go since you can call any OS syscall from Go. It just leads to awkward code.
There's shared memory multithreading with locks to protect data structures. You can do that in Go (except you use goroutines, which are multiplexed into OS threads by Go scheduler, instead of using OS threads directly as in C).
And then there's Go's prefferred (but not exclusive) solution of channels/goroutines and idea of sharing memory by communicating (as opposed to communicating by sharing memory as in threads/locks based model).
What concurrency models are available in C or Python or Java that you can't do in Go?
> So is my Radio Flyer Wagon. You still can't do systems programming, embedded, high performance, or real-time work with it.
As a rebuttal (?), it doesn't follow. As to your point, your definition of systems programming is different from that used by Go designers, since they are very insistent on calling Go a systems programming language. Please clarify what kind of systems programming you can do in C/Java that you can't do in Go?
As to high-performance: I don't follow. It's fast. What kind of high performance programming you can't do in Go that you can do in C or Java?
As to embedded - sure that niche is owned by C. How is it different from Python or Java and how does that make Go a niche language?
The same goes for real-time where real-time systems are even more niche and are as much a property of the OS as it is of the language. You can't do real-time in any language on stock Linux kernel given that kernel can pre-empt any application at any time for any period of time.
You make a lot of statements but nothing concrete enough to support them.
I've shown that in Go you can use 3 concurrency models, 2 of which are currently most popular in the C/Java worlds.
As to systems programming - you don't provide a definition or examples of what kind of programs do you consider as systems programming so it's hard to argue at that level.
Does a web server qualify? (you can write one Go).
Would a distributed database like HBase qualify? Even though one hasn't been written, HBase is Java and there's nothing that you can write in Java that you can't write in Go, with potentially better performance due to compilation to native code and more efficient memory usage.
Being able to write something in Go is besides the point. It being turing complete and having the essential faculties and libraries for a basic subset of tasks, yes, you can write a web server in Go.
Doesn't mean you should.
In any realm where real-time is a concern, where performance is the utmost concern, or where you need real control over the hardware, Go is inappropriate.
It's also inappropriate for Rapid Dev.
>I've shown that in Go you can use 3 concurrency models, 2 of which are currently most popular in the C/Java worlds.
No. C and Java aren't the same world, and your idea of popular is absurd.
I'm out of this conversation, sorry.
Rob Pike? Ken Thompson?
You overestimate the power they have in a company as highly political as Google.
Not surprising, since it is a very young language. However, as a counterpoint, I do.
That's the basis upon which I choose to dismiss Go for a selection of uses unless they make some fundamental changes.
In their latest blog post, they've noted that it's not quite as niche by usage as they thought it would be.
I like Python, but FP programming is a second-rate style there.
Haskell has very good concurrency solutions like sparks(async), MVars and forkIO (threaded), and data parallelism. Writing a program for 128 cores doesn't look very different than writing a program for 2 cores. I think that this will become very important sometime in the next decade as computer will come with more and more cores.
http://lua-users.org/lists/lua-l/2010-11/msg00241.html
The lack of try/catch is also a nuisance.
error("error tag") is comparable to raising an exception.
I wasn't aware of LuaJIT's 1gb limit, but it's neither hard nor unusual to run several Lua states in multiple worker processes.
For libs, I usually check http://lua-users.org/wiki/LibrariesAndBindings and http://luarocks.org/repositories/rocks/ .
I've written a couple libraries, as well. See e.g. lunatest (http://github.com/silentbicycle/lunatest, an xUnit-like + randomized (QuickCheck-ish) testing library) and Tamale (http://github.com/silentbicycle/tamale, Erlang-style pattern-matching for Lua).
Good work on Tamale (and great README btw). The more I program in Erlang, the more if/else nests feel unnatural in imperative languages.
wat?
Also: Common Lisp, Scheme, Erlang, SML, OCaml, Haskell, Lua, Prolog, and Java, and no doubt numerous others.
For all of the above, many (if not most) implementations compile natively.
We should make this more available to the programmer! It should be possible for a programmer to highlight a section of code in an IDE and choose "Apply Trace," whereupon the IDE will apply static type annotations based from saved runtime trace information. The IDE should be also able to present the same information as profiling data to let the programmer quickly home in on the 15% or so of the app which is most performance critical. Using techniques like this should let us get C-like speeds from many dynamic languages.
(Admittedly, this sort of thing would also be highly dangerous. One couldn't apply such semi-automated annotations in ignorance. This could also break otherwise correct programs.)
In other words, a good programmer should be able to just let the VM know ahead of time what's up in critical sections of stable production code. Even better, we should be able to combine datasets from many different runs of many VM instances! This sort of technique would allow us very high degrees of confidence for things like web apps in server farms, where getting 10's of thousands of runtime tracing datasets would be easy to do.
This is where a dynamic language with optional type annotations would really shine.
Snippet from [0], which is unfortunately temporarily down (I got to it through Google Cache[1]):
cpdef double integrate_f(double a, double b, int N):
cdef double dx, s
cdef int i
dx = (b-a)/N
s = 0
for i in range(N):
s += f(a+i*dx)
return s * dx
[0]: http://www.behnel.de/cython200910/talk.html[1]: http://webcache.googleusercontent.com/search?q=cache:7NvoUHh...
To be fair to the author, I'm going to assume that the most esoteric (read: non-C-like) language he knows is probably Python, because he doesn't compare it to anything besides Python, C, C++, or Java. I also assume he left out Java because by "compiled languages" he meant "compiled to native code without relying on a runtime which is external to the compiled application." He's still missing a whole swath of compiled languages, but there is a point of view from which his comment makes sense, i.e. native-code compilation of C-like systems or applications languages.
("...you will find that many of the truths we cling to depend greatly on our own point of view.")
There. Manual memory management. What? It's not as if when you free() something that was malloc()'ed, it gets returned to the operating system right away. It just goes back on the free list. Garbage collection amortizes that.
x = Foo()
...
del x
as manual memory management, after a fashion (which it is). The kind of manual memory management I'm concerned about is less about having precise control over when memory passes in and out of my program's control, and more about controlling fragmentation and time spent searching for unused memory. For example, would slab allocation--an extremely useful technique for operating systems, databases, &c--be possible in Go?Addendum: I'm not trying to denigrate Go, and I don't mean to suggest having or not having fine control over memory makes or breaks a programming language, because it's a valuable tool in certain instances and a hindrance in others. I honestly don't know whether or not it's possible in Go; I suspect it is, to some extent, although it would no doubt be discouraged by the design of the language.
I still haven't read one article from Go aficionados that hasn't made me bang my head against a wall repeatedly.
I don't ever have indentation errors - or if I do, they aren't ever of a sort that pyscripter can't spot them.
Must be a what-you're-used-to thing...
I would argue that this is actually a feature of the language. This "danger" actually forces the programmer to read through the code he is pasting by indenting.
Also, how many pairs of source code fragments share the same variable names? This argument is quite shallow.
A good old ggVG= solves that though in vim
Read through, or partly rewrite? More of the latter than the former, IMO. If you're only reading then 4 spaces is often indistinguishable from a tab.
Sounds like the "block indenting" style in Smalltalk. Kent Beck favored it because it made bad Smalltalk code look bad. (Hard to read.) I like the idea, but in reality, it's too idealistic and not nearly practical enough.
A couple of years ago, however, I tried to help a friend trace an error in code on an active server. It came down to rewriting the code so that it looked exactly the same as before but somehow had the right whitespace instead of the wrong whitespace.
That experience left me with the strong belief that Python's whitespace approach is totally wrong and broken. Things that break in ways that are difficult to understand I can accept. Things that break in ways that are impossible to understand as such are unacceptable.
Yes, the whitespace thing can always be fixed - except on the random server where it desperately needs fixing. Systems where symbols that are meaningful don't appear are broken - it's not that a given problem with said symbol will appear often. It's how extreme the problem is when it does appear.
Given this, it's impossible for me to understand your claim that the white space approach is 'totally wrong or broken'. The leap from vague example (with a sample size of one) to all encompassing abstraction is too great.
Having said that, I'm keen to ensure that I don't pursue the wrong path. I picked python because many people I respected said it was a good 'jam for beginners'. But that can't be true if it's 'totally wrong and broken'.
Care to pony up with a proper account of your position?
The fix is to simply accept only tabs OR spaces as valid whitespace... then again, this is one of the things that annoys me about make, but I digress..
I think we solved this problem like, 20 years ago or something.
But all the fixes depend on completely controlling the environment and the code production process. They work but they add "global control" need that is a bit more extreme than other languages. This qualifies as a "weird fragility" in my mind. Considering how many other levels of any given system might have weird fragilities, why be willing to accept language that guarantees you'll need to deal with such fragility by it's very structure?
I'm sure I over-stated the problem when I first stated it ... but whenever I remember the situation, I feel the terribleness of it so I don't think it's unimportant.
I'm starting to get to the point of just wanting sexps everywhere...
* And after debugging production issues with proprietary compilers that had almost perfectly reliable hot-code loading...that's pretty minor. Not mixing up tabs and spaces probably covers it.
What does that even mean?
The lack of type hierarchy makes code easier to understand.
A type hierarchy seems to get misused almost as often as it's well used. The net benefit of it is much less than what the inventors imagined.
anyone have any kind of measure on how bytecode compares to machine code these days? (most "interpreted" languages have a hidden compile step these days). I understand that this question is largely subjective to the runtime and the task being executed.
Python's bytecode is very high-level and basically just saves the expense of parsing the code. The interpreter is not JIT-ed and thus very slow (compared to machine code).
Java's bytecode is much less dynamic. 'a + b' can execute arbitrary code in python, but in java it is known at compile-time whether it is addition between native types or string concat. Java has a very good JIT compiler and thus can be as fast as 1.5x the speed of c.
The Interpreter/VM distinction had become hazy and lost its meaning years ago. I think people still hold onto it only as a means of excusing poor language performance.
Speed? http://shootout.alioth.debian.org/ is an up-to-date, super detailed answer to that question. A rule of thumb simplification I go by is: current Python and Ruby implementations are at least 10x slower than C doing algorithmic work.
A really good bytecode VM can, of course, get much closer to C performance (as shown by Java, C# or even modern JavaScript implementations) especially if JIT is given enough time to profile the app at runtime and generate highly optimized code based on that profile data.
There are of course other aspect you can compare. Bytecode is, inherently, cross-platform and machine code isn't.
Bytecode is usually more compact than equivalent machine code (but then you need the constant overhead of the runtime to interpret that bytecode).
The realy argument, of course, is that in a given time frame, with people of similar skills, you will not have the same implementation unless your team contains only vulcans. Several people in the scipy community have reported having gone from C++ to numpy/scipy and went faster at the same time - because C++ is so hard to use correctly, people whose job is not even programming ended up doing things very fast but one millions times because they don't understand their code.
This point is surprisingly not understood by a majority of programmers. Most of the time, you see benchmarks for some trivial or even non trivial algorithms, well specified, and get "look, this language is N times faster". But in my experience, this almost never happens in real life - code specification keeps changing, you need to redesign constantly what you're doing.
> The documentation is very good and can be consulted instantly form the command line or from a browser, indifferently.
I can't speak for Go, but I've always found Python's documentation to be fantastic. And very few things make me as happy as docstrings.
Over my dead body!
Is writing if a { .. } else {} so much worse than if a then .. else .. end (Ruby requires then/end instead of {}.
Python's indentation based syntax (which I think is great) cleverly solves the problem of requiring explicit statement delimiters but it's also a reason why lambda: is limited to one-liners. There's no free lunch.
To Go's credit they did put a lot of effort and design thought into removing line noise from syntax (compared to C) by e.g. adding an automatic semi-colon insertion rule, eliminating braces around if argument etc.
Overall, while syntax is not as clean as Python's, it comes pretty damn close.
package main
import "fmt"
func blah() bool {
return false
}
func main() {
x := 5
if(blah()) {
x++;
}
fmt.Printf("%d\n", x)
}
(which prints 5) and then this: package main
import "fmt"
func blah() bool {
return false
}
func main() {
x := 5
if(blah())
{
x++;
}
fmt.Printf("%d\n", x)
}
(which prints 6). Looks like a major problem to me, and at any rate it's a far cry from having made real (or any) progress. Python _did_ get whitespace right.In any case, language syntax should always take into account the expectation of programmer communities. If you push syntax too far, you lose too many people.
When D was nascent, many people proposed various schemes for making semicolons optional. I think it's great D didn't fall for such.
The most popular languages are C, C++, Java and C# and Go's syntax is very similar to those. If anything, Python is the weird one in how it treats the whitespace and hence most likely to confuse people coming from other languages.
As to optional semicolon elision - what is the problem with that? They make the code cleaner looking and gofmt will remove them for you even if you forget due to C/Java reflexes. I don't see the downside of making them optional.
That truly speaks poorly of you.
OTOH, the problem can easily be fixed by requiring a statement to contain at least one token.
I agree with you that Python's syntax is cleaner but I would like to bring few novel syntax-related things that Go did that are not as commonly known and in some cases are improvements even over Python.
In general I don't like deep indentation and Go has few things to help keep indentation to a minimum.
1. defer statement
In Go you can say:
f := os.Open("myfile.dat", ...)
defer f.Close()
That guarantees that f.Close() will be called at function exit. In Python/Java/C# to get the same guarantee, you would have to use try/finally block, introducing additional indentation for the code using f.2. Syntax for defining methods on interfaces
func (p *MyClass) Method(int i) {
return p.x + i
}
The equivalent in Python would be:class MyClass(): def Method(i): return this.x + i
Again, less indentation.
3. panic()/recover() way of handling fatal errors also doesn't introduce additional indentation level like try/catch blocks.