What Python developers need to know before migrating to Go
blog.repustate.com
blog.repustate.com
This statement needs to be qualified.
Switching from an interpreted to a static language will catch a certain class of bugs upfront. I've rewritten some small programs from C to Go, and the same logic bugs are present in both.
In this sense, switching to Go is no different than switching to Java/C in terms of the bugs it will catch. There's nothing in Go that sets it apart from other static languages in this respect (unlike Haskell).
> It’s very similar to Python in terms of verbosity (or lack thereof) and it treats functions as first-class objects, so functional programming is easy to reason about.
Where is map, reduce/fold, filter, scan? Go doesn't support functional programming primitives due to lack of generics.
I may not have written enough Go programs, but I find Go marginally less verbose than C thus far (ignoring concurrency). Channels and goroutines and are its most interesting features.
In fact, a friend of mine did a Go library to write functional-style code: https://github.com/tcard/functional
If function pointers are sufficient to qualify a language as a functional, then that would include C/C++.
In contrast, Rust has support for the standard functional language primitives.[0][1][2]
[0]: http://static.rust-lang.org/doc/core/vec.html#function-map
[1]: http://static.rust-lang.org/doc/core/vec.html#function-foldl
[2]: http://static.rust-lang.org/doc/core/vec.html#function-filte...
It's got some of your primitives (map and filter but no fold, but I'm assuming that it's just a matter of getting bored before implementing it) and closures.
It's not a serious project, but it serves as a counter-example.
Nobody's questioning that a map-like thingy can be implemented in any Turing-complete language you choose, the question is whether it's actually useful in the context of the rest of the language. I can in fact write a generic map function in Go, by using reflection, but it'll be very slow, and it will still absolutely require the user of the map function to cast the result into the proper type manually since the Go compiler can't do it for you.
(You actually can do something like closures in C, though managing the lifetime of the closure data struct can be tricky. And that's actually the problem with functional programming in C; the functional style does not technically "require" GC to work properly, but it sure does encourage it awfully strongly. Or you need something like Rust, with strong support for explicit lifetime management. In C you're just begging for memory leaks with a functional style.)
The question is not whether you can do something, it's how many hoops the language makes you jump through to do it.
Informally, my definition of a functional programming language is one that supports functional style as the default. (Hooray for using a recursive definition. :p) The language should strive for referential transparency and the language and compiler should be optimized for functional idioms.
Let's define the sum of a list functionally[1]:
# Python
sum = lambda x: reduce(int.__add__, x, 0)
# Haskell
sum = foldl' (+) 0
You could write this same function in C using function pointers and arrays.
However it's not very convenient to do so. You would need to write a reduce
function, an add function, anonymous functions don't exist, and syntactically it
would be very messy. On top of all this, the compiler is not optimized for
reduce functions vs an imperative for loop.[2]More generally, functional style typically refers a style of programming where most functions are pure and side effects are contained specifically. This allows for easier reasoning of a program's correctness.
John Carmack has a post about functional programming in C++:
http://www.altdevblogaday.com/2012/04/26/functional-programm...
Summed up, functional style is about containing side effects and functional programming has built-in support for common idioms.
[0]: https://en.wikipedia.org/wiki/Functional_programming
[1]: This ignores the fact that sum() is already provided in most functional languages.
[2]: Python straddles my definition of functional programming and that's why it's awesome and frustrating at the same time. It supports many staple functional idioms but is not optimized for it. Function calls are very expensive, and thus recursive performance is also hurt tremendously.
Plus Guido hates functional programming:
http://python-history.blogspot.com/2009/04/origins-of-python...
Or use other languages that are more developer friendly.
But in terms of more 'mass market' languages, C++ and C# both have incredibly robust generics that can be used to do real, no-foolin' functional programming, though doing it in C++ certainly takes a lot more effort and leans on modern libraries - closures are a comparatively recent introduction to the language. You've also got 'mass market' functional languages like F# to have fun with.
Then there are the more fun options, like writing your functional code in Lua or Python and running it in a high-performance runtime like LuaJIT/PyPy to get fairly competitive performance without even needing explicit typing or built-in 'generics'.
- C++
- Haskell
- Modula-3
- Ada
- Java
- OCaml
- Delphi
- F#
- Virgil (from Google)
- VB.Net
- Eiffel
- Sather
- Zonnon
- Scala
Just a small list.
I don't see a lot of C/C++ developers switching, probably because they have sunk costs with their respective languages and Go doesn't offer very much by comparison.
I would definitely use Go where it seems appropriate, but I don't see it replacing C as a systems language.[0] Sometimes when I write something in Go, I look back and wonder why didn't I just do it in C. However, Go is great in a distributed systems / networking environment where most new code seems to be written.[1]
In the words of a friend, "You can pry raw memory access from my cold, dead fingers."
[0] systems == OS, drivers, etc.
[1] Motivations summarized in the abstract: http://talks.golang.org/2012/splash.article#TOC_1.
This is true, but there is one piece ~unique to Go: type inference. It doesn't exist in Java or C, and it's only just made it to C++.
For an individual coming from an interpreted language, it makes an otherwise statically typed language feel a lot less unwieldy and bureaucratic.
Have a look at Native Oberon and AOS, both desktop operating systems developed at Zurich's technical university.
They are done in GC enabled systems programming languages and were used both as desktop systems by the teachers, as well as to teach operating systems classes.
Quite nice desktop environments using strong typed languages, but offering a Smalltalk like user experience.
http://www.inf.ethz.ch/personal/wirth/books/ProjectOberon.pd...
https://www.ics.uci.edu/~franz/Site/pubs-pdf/BC03.pdf
http://www.ocp.inf.ethz.ch/wiki/Documentation/WindowManager
The problem with any systems programming language is that it is only used as such, if an OS vendor makes it the only way to do OS programming. Like Microsoft does with C++ or Apple with Objective-C.
Well, one has to question then why no vendor made something like Oberon etc "the only way to do" his OS programming.
Perhaps because having POC OSes at some university wasn't enough, and performance was lacking of OS level work, and lacking in a way that Moore's law cannot address?
(E.g we see Go pausing for 1-10 secs every few minutes on large memory loads. Which might be hood for an academic OS, but not for the OS I want to run video editing, audio DAWs, large apps and application servers on. And even the attempt by SUN to write something as basic as a web browser in pure Java was dog slow circa 2000).
If Sun did not invest in JIT research Java would already be gone, the same for JavaScript JIT research.
Having used those systems I can say they were fast enough to be able to display videos. The codecs make use of Assembly, but the same is true when C is used.
Those languages were just victims of the C trap, were the OS copied the UNIX model and thus were being developed in C as well.
Which mainstream OS vendor can you imagine bringing an OS to the market, with an alternative systems programming language and being able to succeed at it?
If the language is good for this purpose, and the resulting OS is better, as in equally fast AND far more secure than the current ones (a marginal increase in security won't matter at all), then any big vendor.
If the OS is feature complete and can run current apps (even if recompiled) with a compatible system call API, then why not?
But if Microsoft with all their resources can't make it performant enough to be of interest outside of academia (where Singularity ended up) then I doubt anyone can.
The thing is of course that native non-garbage collected operating systems aren't crash-prone, if they were then we would have seen a shift in systems language out of pure necessity ages ago. As it stands, bugs are found, bugs get fixed, systems end up stable and very performant.
C is official legacy and its compiler won't be improved past C90.
C++ got the C++/CX extensions, which kind of make it language with integrated reference counting, if you only care about Windows. Assuming Windows 9 or whatever it will be called will have broader WinRT support and better commercial luck than Windows 8.
.NET applications are compiled to native code in Windows Phone 8, by making use of techniques developed during the Singularity project. So they got something out of it already, even if it is not at kernel level.
However it is a case of "Worse is Better" and I personally think it will take a few generations for young developers to replace the old mentality. Or I am just plain wrong.
I reckon it is for the same reason we're still mostly running on PC-compatibles with an x86-based architecture. The only reason for that has been compatibility, not any kind of inherent technical superiority.
> Something as basic as a web browser
I am puzzled at how you can make such a statement. That is definitely one of the more complex programs running on a typical desktop.
Go might need to improve its gc to prevent the occasional stutter, but it would otherwise be sufficient.
However my original statement defined systems as kernel code, since HN tends to have many opinions on the topic.
> The problem with any systems programming language is that it is only used as such, if an OS vendor makes it the only way to do OS programming. Like Microsoft does with C++ or Apple with Objective-C.
I see plenty of new C code still be written outside of systems, the problem is lack of exposure. HN community is fairly web focused but HPC, scientific computing, graphics, engines are still predominantly done in C/C++.
That is how I understood it. Have a look at the Project Oberon book.
Assembly is only used for the boot loader, device driver glue and some GC implementations. The same type of stuff you need language extensions in C as well, as they are not part of ANSI C.
Everything was coded in Oberon or Active Oberon, depending on which OS version we talk about.
This startup is selling embedded compilers for Oberon that target 32 bit ARM since the late 90's.
http://www.oberonday2011.ethz.ch/talks/armembedded.pdf
The trick is to have control when the GC happens and to allow for manual memory management via a SYSTEM pseudo module.
But yes, in the current state of affairs C and C++ will outlive us anyway.
Desktop applications are not very demanding. You can write them in scripting languages like Python or JavaScript, both has been done in Gnome. The libraries/toolkits are more demanding.
> Haskell has proven gc language is fast enough for a WM (xmonad).
https://github.com/BurntSushi/wingo
> However my original statement defined systems as kernel code
Rob Pike (one of the Go creators) seems to define it differently:
"We designed it to be a systems level language because the problems we do at Google are systems level, right? Web servers and database systems, and storage systems and those are systems. Not operating systems, I don't know that Go would be a good operating system language but I am not sure it wouldn't be, what was interesting was because of the approach we took in the design of the language, somewhat to our surprise it turned out to be a really nice general purpose language." http://www.infoq.com/interviews/pike-google-go
Anyway, popularity isn't the same thing as tyranny, there isn't some conspiracy to keep other languages down
Look where C is being written today, mainly in libraries or very/extremely performance oriented applications and low level systems programming where any kind of unecessary overhead (performance, footprint, dependencies) is actively being avoided. Where does Go fit in here?
Instead Go has mainly seen an uptake in web oriented development, I personally think it also has great future in desktop style application development, but that remains to be seen.
Actually I rather vote for D or Rust, after a few disappointments with Go's design.
What I will ALWAYS defend, regardless of the language, is the use of GC enabled systems programming languages.
I am an old dog and have used quite a few operating systems in academia and also internal in-house ones developed in such languages and know from experience they can be usable if only an OS vendor cared to support such languages.
I want my Lisp Machine back :)
Yeah, "seems to be correct" is a little wishy washy... I'd argue that Go has enough syntactic sugar to be easy to learn from a Python background, but enough structure that it encourages good software engineering practices.
The whole paragraph is a qualification. How much more do you want??
Ironically, Eric Raymond made very similar statements _about Python_ in his 2000 article explaining why he had moved to Python from static languages and from his previous dynamic language of Perl:
"When you're writing working code nearly as fast as you can type and your misstep rate is near zero, it generally means you've achieved mastery of the language. But that didn't make sense, because it was still day one and I was regularly pausing to look up new language and library features!"
"This was my first clue that, in Python, I was actually dealing with an exceptionally good design. Most languages have so much friction and awkwardness built into their design that you learn most of their feature set long before your misstep rate drops anywhere near zero. Python was the first general-purpose language I'd ever used that reversed this process."
____and again, regarding a little program whose code he links in the article____. . .:
"That doesn't look too bad for deep black magic, does it? Thirty-two lines, counting comments. Just from knowing what I've said about the class structure, the calling code is even readable. But the size of this code isn't the real shocker. Brace yourself: this code only took me about ninety minutes to write—and it worked correctly the first time I ran it."
"To say I was astonished would have been positively wallowing in understatement. It's remarkable enough when implementations of simple techniques work exactly as expected the first time; but my first metaclass hack in a new language, six days from a cold standing start? Even if we stipulate that I am a fairly talented hacker, this is an amazing testament to Python's clarity and elegance of design."
"There was simply no way I could have pulled off a coup like this in Perl, even with my vastly greater experience level in that language. It was at this point I realized I was probably leaving Perl behind."
This is the feeling I get from good strongly/statically typed languages too. My best example is a ray tracer written from scratch in haskell. It took a good couple of hours, but then once it actually compiled it run the first time without error (but rendered everything behind the camera) and exactly as it was supposed to work on the second try.
I really like languages which give me that kind of feeling.
I did C/C++ programming professionally for more than 10 years (and come from a static language background) and I share the OP's view that compilable Go code is much more likely to be correct than code in other languages I've used, even static ones... the fact that the Go code compiled is of course no guarantee it'll be correct, but the Go compiler enforcing the rules of the Go spec does eliminate whole classes of common gotchas even ones that bite you in C/C++ and other compiled languages.
The lack of exceptions and forced handling of errors means you have to think of error handling immediately. It adds verbosity to code but also means most potentially failing calls have been explicitly handled, even if it's causing a fatal error and printing a message. Go considers unused variables an error, and while that can be a pain sometimes, it's also caught a few bugs where I've used an initialize and set operator (":=") in an inner scope, which didn't set the same variable in an outer scope.
The main thing I wish Go had is generics.
1. "Different assignment operator is used depending on whether you are inside & outside of function ie. = vs :="
= is an assignment, := is a declaration and assignment at the same time. It's true that you can't := outside a function (which I think is silly), but the comparison should be between these two:
var foo string = "Hello"
foo := "Hello"
2. "Else (or else if) has to be formatted properly, where the else is on the same line as the curly bracket from the if clause. Weird."Whereas, coming from Python where indentation matters. Weird :-) Both are style conventions enforced by the language.
(Honestly, why can you not 'for var x = 0; ...'? and only 'for var X := 0; ...; ...' but immediately below var x = 0; is just as valid. The := / var syntax in go is just plain weird).
Go is also the only place I've ever seen this syntax:
if blah {
..
} else {
..
}
Maybe that's popular in some obscure places, but it is weird they made that the only valid syntax.I mean, fair enough, Go has its own syntax, but these are pretty valid gripes.
Do you mean
if blah {
code
} else {
code
}
as in parens-less if statement? Because I don't see anything odd in what you wrote, it's valid C or Java.And if it's putting else, } and { on the same line, it's "standard" K&R, 1TBS, BSD KNF and java indent style/coding conventions.
if blah {
code
}
else {
code
}
Personally, I don't prefer the K&R way but I don't think it's either strange nor troublesome enough to worry about for more than a few seconds.> Go is also the only place I've ever seen this syntax
compounded by
> Maybe that's popular in some obscure places
(thus calling Java's coding conventions or the K&R "obscure places")
If Go enforces a specific coding convention at the compiler level, then that could legitimately be considered at least slightly unusual.
While it's true that this style is far from esoteric, it's also typically just that ( a coding style ). This strikes me as at least somewhat analogous to Python's whitespace indentation rules or Java's checked exceptions, in that what is typically a design choice is enforced by the compiler / interpreter.
Hopefully this new topic has less staying power.
Not to mention that's a well known style, has a lot of benefits (less wasted lines compared to the style that puts both brackets on empty lines, less potential for errors in than when you omit the brackets for a single statement), and, most importantly, puts and end to all bikeshedding.
Especially along with gofmt, go is a bikeshedding free language in regards to syntax.
And it's part of Sun/Oracle's official java coding conventions.
I don't really understand your 'come on'. I wasn't disagreeing with the OP about point #1, I was merely correcting what he said to make it clearer and I even agree with you (see my note about 'silly) that where you can and cannot use var and := is odd and unclear.
As for the concern about Go syntax and brace positioning, it will really depend where you come from. If have any experience of any note in any C-derived language (such as C++, Java, JavaScript, etc.) you will have seen people writing code like that for years.
Because var and := declaration+initialization are semantically different: when declaring multiple, variables, := allows redeclaration (which just overwrites an already declared variable's value instead of shadowing it), while var does not.
> Maybe that's popular in some obscure places
Err, K&R style (from which Go's style is derived and virtually identical, except for braces of functions) isn't exactly obscure... even the Java's Coding Convention (from 1999) does it exactly like that: http://www.oracle.com/technetwork/java/javase/documentation/...
K&R had it back in the 70s.
if blah {
..
} else {
..
}
> Maybe that's popular in some obscure places, but it is weird they made that the only valid syntax.That is valid K&R syntax[0], which is used prevalently in C (e.g. Linux kernel). FYI, Rob Pike wrote a few books with Brian Kernighan (the K in K&R).
Go is designed for consistency and optimized for the compiler (e.g. mandatory braces, no unused import statements). Declaring a single canonical format (e.g. gofmt / PEP 8) solves a lot of syntax bikeshedding.
Even ignoring that benefit, taking a stance on style like Go has done allows style to be handled mechanically, which is more in line with the general hacker attitude anyway. Offload that stuff to the computer; let the programmer concentrate on doing the things the computer can't.
Then you should go read some more code. Because not only it's the one I've been using for ages (hah), but it's standard K&R style, one of the oldest and most established styles out there...
blah.Go().
Go().
Go()
Which is equivalent to: blah.Go().Go().Go()
I don't see why it would be significantly breaking to allow a } to similarly avoid the automatically inserted semicolon for if statements.This wasn't a 'consequence'; this was a design decision.
A design decision with a sound reasoning: https://groups.google.com/forum/?fromgroups=#!topic/golang-n...
As for the other points, it would be nice to have more built-in types like a Set type, to be able to ignore "unused dependency" errors when running "go install", etc.
Oh, and I think he missed the most surprising thing about Go for newcomers: that "if" creates scope!
Go is block scoped, and if statements are blocks. I don't see this as surprising.
I agree some set operations would be nice, maybe using an interface like Sort, but you can use a map as a basic generic set data structure. This is how many language implement a set anyway.
I meant surprising for someone used to Python, like the article author. I'm surprised he didn't mention it.
Python, Ruby, and PHP have function scope, but idiomatic Ruby guarantees block scope for loops. In Python even list comprehensions leak scope.
JavaScript has function scope but has the `var` keyword for local scope. Lua has global scope but has a `local` keyword as well.
As I mentioned before, however, I was referring to developers principally familiar with Python (or at least dynamic, interpreted languages) coming to Go, since that is the basis of the article.
Also your wording about javascript is weird. 'var' for scoping is function scope, what is your 'but' for? And proper lua doesn't use globals for temporary variables, it's firmly in the 'block scope' camp.
The article's title is "What Python developers need to know before migrating to Go". I don't know how many times I need to repeat this. I can't go back and edit the original post to clarify.
Anyway, about JS, you are right; I was thinking "var" was like "local" or "my", but it isn't. Oh well.
Go has different syntax and rules than python, but so does C, Java, Erlang, and so on. I don't get why this is such a surprise. Don't approach a new language assuming you can directly translate from your last language du jour, and you'll be much happier.
http://webcache.googleusercontent.com/search?q=cache:http://...
It would be nice to see official constructors in Go, to encourage consistency, it feels a little ad hoc at the moment.
You can’t have mixed type data structures. Maybe it’s not kosher, but sometimes in Python I’ll have a dictionary where the values are a mix of strings and lists. Not in Go, you have to either clean up your data structures or define custom structs
I didn't understand this point. If you want you can use a map[string]interface{} to store arbitrary mixed types in a map (dictionary), though obviously no type checking is then done on the dictionary, but you could define an interface Inter which both types conform to and use map[string]Inter instead so that you know what you're getting and that it will respond as you expect.
If I want a list of just the keys or just the value, as in dict.keys() or dict.values(),
It'd be nice to see some enhancements to map for common operations like this, it's something I missed from Ruby. It'd be nice to have an ordered map in the standard library as well.
Else (or else if) has to be formatted properly, where the else is on the same line as the curly bracket from the if clause. Weird.
This sort of grammar quibble mystifies me - one of the nicest things about go is gofmt and the recommended formatting - I like that, for trivial points of grammar like this, there's only one way to do things which is considered correct. This isn't the style I'd personally use elsewhere (in C for example), but it's great that all Go code is formatted in exactly the same way - it makes it easier to read, and really is not hard to pick up.
If you’re using JSON and your JSON is a mix of types, goooooood luck. You’ll have to create a custom struct that matches the format of your JSON blob
This does have the advantage that you know what you're getting, and can't unmarshal unexpected objects by mistake when fed malicious/broken JSON.
So perhaps it is more of a sacrifice that is getting smaller day by day?
Also you can unmarshal JSON into an interface{} variable and get a mix of map[string]interface{} and []interface{} that you can deconstruct via type assertion if you know what to expect, so you can be a bit python-ish about JSON if you don't want to read it into structs.
Actually, you should use map[rhubarb]struct{} since a struct{} requires no storage. You can test membership with this:
_, ok := m[strawberryRhubarb]I believe you can use json.NewEncoder().Encode() to arbitrarily deal with interface{}'s
ETA: I realize that it just looks like I'm spouting opinions if I don't get into why it would be badass. Essentially, Go is a language designed for production systems in one of the most demanding large-scale software environments on earth. Compilation is rapid, and the language does a lot of things that force code quality. The problem is that it still feels a bit verbose. Putting a Lisp on top of that (and Clojure is my favorite Lisp) would be really interesting.