
Things from Python I'd miss in Go - smackay
http://www.yosefk.com/blog/things-from-python-id-miss-in-go.html
======
jgrahamc
Towards the end of the article there seems to be a confusion between servers
and web servers. Yes, Go is nice for writing servers, no, web servers aren't
the only things out there doing 'serving' in systems-land.

Three examples from CloudFlare all written in Go:

1\. Our Internet compression/optimization technology called Railgun

2\. Our DNS server

3\. Our CA infrastructure

All are networked, all are highly concurrent.

Also, our entire logging and analysis infrastructure is being migrated to Go.

PS Forgot that we also recently rewrote the code that does image compression
(using C tools) in Go as well. Here the Go code is working as a job server.

~~~
jamra
It looks like the author found the worst use case for Go in comparison to a
library that Python absolutely soars in and uses that as a basis for this
argument. Go does not have something as nice as numpy. For numeric computing,
by all means use Python.

He then makes some crazy statements about operator overloading as if that is
the essence of a good language. I disagree. I don't want operator overloading.
I almost never used operator overloading in C# or Java. Maybe that's just me,
but it's certainly not a reason to avoid a language.

The crux of his argument is that C++ users who switched to Java years ago are
the types of programmers that Go wished to convert.

He neglects to mention deployment. He neglects to mention stability in
runtime. He neglects to mention any low level meddling that you have to do in
compression. The examples you posted are exactly the kind of things that Go
soars in. It's very convenient to leave them out while beating your chest
about how your language is a superior language because it is superior in a
very specific niche.

~~~
S4M
> I almost never used operator overloading in C# or Java.

Dude, you can't overload operators in Java. You can overload methods of a
class, but not operators. Operator overloading means that you can overload '+'
for example for your particular class, which would enable you to write code
like (in Java):

    
    
        Matrix a = zeroMatrix(4,4);
        Matrix b = identityMatrix(4,4);
        Matrix c = a+b;
    

You can't do that, so you would have to write:

    
    
        Matrix c= a.add(b);
    

Which makes your code verbose and makes Java (and Go) painful for doing
numerical analysis.

~~~
nordsieck
It's important to note that numerical calculation is a domain particularly
suited to operator overloading simply because the underlying domain (math)
already uses operator overloading heavily (e.g. multiplication means something
different when done on scalars and vectors, but in both cases it is well
defined and in common usage).

~~~
Someone
You should leave out the 'overloading':

\- Math (and it's very close relative logic) are the only domains that use
operators, period.

\- other domains use overloading; homonyms are very common.

------
Kurtz79
Sometimes we get so invested in a language that we forget that it's just a
tool, and a good engineer/hacker should try to choose the best one for the
problem at hand.

Go is just another tool in our toolbox: as the author says it sacrifices some
of Python's friendliness and ease of use (but not as much as other
compiled/statically typed languages) and features for performance and a solid
concurrency model.

It's up to us to decide what the best fit for our applications is.

Even if Go it's definitely not "The Language to rule them all", it's nice to
have more options.

~~~
klibertp
I try to advocate polyglot programming - using the best tool for the job -
everywhere I go, but unfortunately people are largely allergic to the idea.
Most programmers don't want to learn new tools, much less new paradigms or
methodologies. Even worse, the management agrees - the upside of building
better systems isn't convincing enough to justify a temporary drop in
performance while learning.

More to the point, I don't understand why the OP wrote this article in the
first place. It's obvious that Go is not designed to support his use-cases and
that's really all there is to it. Listing reasons why Go is bad at doing
things it wasn't meant to do seems a bit pointless to me. It's like bashing a
screwdriver for how bad a hammer it makes...

~~~
gejjaxxita
The drop in performance isn't just while learning. I worked somewhere where I
had to regularly use TCL, Java, PHP, C++ and Python - each was the most
suitable tool for the job (at the time the tool was written anyway) but the
constant context switching caused a real and permanent performance hit.

~~~
klibertp
> but the constant context switching caused a real and permanent performance
> hit

Maybe context switches were too frequent? Or the opposite, too rare, which
could lead to repeating the learning overhead each time? Personally I didn't
notice any slowdown due to switching between languages and technologies -
other then at the beginning, when I was learning them.

~~~
nostrademons
For me, it's the infrastructure & best practices that's the biggest cost.

There are degrees of language learning. I went through a "everyone should be a
polyglot programmer" phase when I had about 3-4 years of experience, because I
was then proficient - not expert - at about a half dozen languages. At that
point, I knew the syntax and semantics of all of them, the common standard
library calls that I needed for everyday programming, and most importantly, I
could mentally map between constructs in my head. So I'd be like "This is a
'for x in collection' loop in Python, a 'for (String x : collection)' in Java
5, a 'for (var x, i = 0; x = collection[i++];)' in Javascript', a 'map (\x ->
...) collection' in Haskell".

I'm coming up on 10 years of experience now, and I try to limit the number of
programming languages I work with pretty dramatically now. What's changed is
that I now think of a language as an ecosystem and a culture, rather than a
set of things I type into a computer. The typing is automatic; instead I'm
thinking of the level of "Well, if I use Mockito and JUnit for my unit
testing, here's how I have to set up my Dagger modules, and I can use
Providers there to give me a dependency graph of ListenableFutures that will
let me kick off a whole cascade of RPCs when a request comes in, all without
having to manually manage the sequence of events." And all of those libraries
have gotchas and best practices that I've internalized, which I think need to
page out if I start working with another ecosystem. (This was perhaps a bad
example because I'm actually much more fluent with Python and Javascript than
Java now - but then, maybe that makes it a _good_ example, because it shows
how much tacit knowledge is important even for a simple forum comment, let
alone a working system.)

So I can't really judge your level of expertise over the Internet. But I'll
caution you that views on this can flip-flop as you gain more experience. It's
important to really learn one language well before judging that everybody
should be able to use multiple languages with equal proficiency. "Learning
them" is a continuous process, and there're tips and tricks that are very
specific to each language that you continue learning even decades in.

[1]
[http://www.reddit.com/r/haskell/comments/cguuj/a_haskell_web...](http://www.reddit.com/r/haskell/comments/cguuj/a_haskell_webserver_in_ascii_art/)

------
adwn
It's ironic that the author dismisses C++ to a small niche:

> _Those who still stick to C++, after all these years, either really can 't
> live with the "overheads" (real time apps - those are waiting for Rust to
> mature), or think they can't (and nothing will convince them except a
> competitor eventually forcing their employer out of business)._

Yet later he gives a good example for an important area where C++ is still
unbeaten: scientific computing libraries.

If you want/need maximum performance, then right now, there's no other
language that has no overhead and zero-cost abstractions (to the degree that
C++, especially C++11, has them). Hopefully Rust will get there eventually,
but as of today, you have three choices: Fortran, C, and C++ – and neither C
nor Fortran offer any abstractions worth mentioning.

~~~
fulafel
Zero-cost abstractions come at a pretty hefty price if you pick C++. The cost
is in programmer productivity & quality of resulting software. I'm sure there
are some fields of academia where this is a reasonable tradeoff (if you're
programming DSP algorithms running on a embedded battery powered device
strapped to a dolphin, say...) but your generic number crunching job rarely
calls for it.

(As an aside, zero-overhead abstractions being unique to C++ doesn't sound
right to me. Then again I haven't heard the term used outside C++ circles and
its meaning is nebulous, so maybe it's just a name for C++'s tradeoffs?)

~~~
adwn
> _Zero-cost abstractions come at a pretty hefty price if you pick C++. The
> cost is in programmer productivity & quality of resulting software._

I agree with you. But as with most prices, sometimes they are worth it. There
are a lot of practical cases where you really want the performance (even
outside scientific computing, which in itself is a pretty widely applied
field).

> _I 'm sure there are some fields of academia where this is a reasonable
> tradeoff_

I'm not sure where you got "academia" from; _scientific computing_ is in no
way restricted to research. Weather predictions, genome processing, and oil
reserves exploration are just three of many examples for the commercial
application of scientific computing.

> _(if you 're programming DSP algorithms running on a embedded battery
> powered device strapped to a dolphin, say...)_

DSP applications are ubiquitous today; you'll find them, e.g., in your phone,
or in your car.

> _but your generic number crunching job rarely calls for it._

You're very wrong. Number crunching is _the_ example for which you want lots
of performance.

> _As an aside, zero-overhead abstractions being unique to C++ doesn 't sound
> right to me._

I didn't say they are. I said that no other language offers them to the degree
that C++ does.

> _so maybe it 's just a name for C++'s tradeoffs?_

No. It means abstractions which do not cause runtime overheads because the
compiler can optimize them away.

------
rakoo
I think the author misses the point. The number one reason Go was created was
to build _maintainable softwares_ , the kind Google uses at large. The easiest
way to build these are:

\- Automatic memory management -> GC

\- Bug catching before the software is run -> Static typing

\- Overall simplicity -> few features, added only if it is extremely needed

The thing is, when you start using Go, you already know its features. There is
nothing particularly new, and it all fits in your head. It is a bit strict
though, so there is some boilerplate (error checking, sort.Sort, ...) but
that's going to save you when you edit your software in 5 years.

Here, performance (both compilation and running) is a byproduct of simplicity.

Now, I'm not saying the OP's use cases are invalid, far from it; they're just
not what was intended in the process of creating Go. Like OP, I tend to think
that Go is the new Java: "boring" (ie no revolutionary features) but it just
works for server-side softwares.

~~~
_yosefk
Why do I miss the point if, _like me_ , you think Go is the new Java? :-)

I think it's exactly that, it works and that's fine, and why the JVM doesn't
do cheap concurrency I don't know. If it did Java might have been the new Java
:-)

~~~
grey-area
Concurrency is not the only interesting thing about go. The other choices are
also interesting, partly in what they leave out (no inheritance, no headers,
explicit errors, implicit interfaces, static binaries with no dependencies,
fast compilation, strict style enforced by gofmt). You might not like those
choices of course, and you might prefer to use other languages like python or
C++ ;), but comparing Go to Java + concurrency is pretty absurd, as the
culture, tools and standard library are very different. Maybe superficially
some of the syntax looks similar, because of the C heritage.

Thanks for the article with your first impressions of Go anyway - I read
another post on your blog while visiting (about leaving C++ for a simpler OO
C), and it actually echoes a lot of the motivations of Rob Pike and others at
Google in creating Go - frustration at C++ compile times and baroque grammar
was a primary factor in the creation of Go, so it feels to me like they went
back to C as a basis and built something new...

------
nkozyra
The error handling one is an area I've never understood - in a well-designed
Go app errors will be returned from functions/methods and you'll either deal
with them or not _/err.

I love try/catch/finally but I fail to see how doing either a _ or writing a
simple log function to do something with returned errors necessarily
represents "more code" than handling exceptions.

~~~
drdaeman
That creates some mostly unnecessary boilerplate. I.e., if an error happens
down the stack in function to read config, which calls a function to parse a
file, which calls a function to read a file, you'll have to either propagate
(or otherwise handle) that `err` manually or lose the precious information.

On the contrary, Java-like pattern of functions like `Config readConfig()
throws IOError, ParseError` simplifies code quite a bit. No need to deal with
IOError in parseConfig - if an exception happens there it'll be propagated
automatically. And `throws` clause allows for static checking and warning
whenever you forgot to handle something. And sane code analysis tools would
also warn you whenever you really wanted overly-broad `catch Exception(e)`,
too.

There's a panic/recover in Go, but they aren't serious. At least, to my
limited knowledge of Go, there are no guidelines on using them properly, so
everyone panics with whatever they fancy, and this lack of conventions is a
bit problematic, like `raise "Failed to open file"` in Python.

~~~
moreentropy
OTOH, if you follow Go's idioms and handle every error where it happens, your
code will look like having a lot of boilerplate at first, but you end up with
better error handling. It's the same as putting a try-except catchall around
every single call in python, because in python you can never be sure (without
reading the source) what kind of exceptions something will throw.

For request based services and something where some big ass operation will
either fail in some way (and i don't care where exactly) or be successful,
exceptions are fine. If something goes wrong somewhere, log it and reply with
HTTP 500. But for servers with data i care about, i most likely want to
recover right where the error happened, and actively decide if i want to abort
and push the error to the next layer. Not having exceptions makes this more
intuitive.

~~~
pekk
You shouldn't be putting a try-except catchall around every single call in
Python. Exceptions mean you don't have to do that.

~~~
moreentropy
For me the most important thing exceptions mean is that they might pop up at
any time, with unpredictable types. So as long as I don't have a catch all
exception handler wrapped around _all_ code, it might crash at some point.

~~~
TheLoneWolfling
Assuming you're talking about _unchecked_ exceptions, correct.

~~~
moreentropy
Python doesn't have checked exceptions.

------
atilaneves
I've been waiting for someone to weigh in on this from the Python side for a
while now. I'm a C++ and Python programmer (amongst other things) and I can't
ever see myself switching to Go. I'd sooner switch to Rust or Swift, but D has
been my favourite language by far since I picked it up last year.

I actually don't understand why, in the absence of niche use-cases, how and
why Python programmer would write Go code and _like_ it. Are these Python
coders who've never heard of itertools??

~~~
sergiosgc
From what I understand, Go is an opinionated language, with a nice concurrency
framework. It's not groundbreaking, it's not particularly expressive or
adapted to any specific use case. It so happens that if your style fits Go's
ideology (opinionated view), you become a fan.

For me, the lack of exceptions kills the language. C-like error testing smells
like a twenty year de-evolution. I also dislike the flat object model, but
could force myself to live with it for the sake of trying composition over
inheritance. Error returning, however, is taking the ideology too far.

~~~
niemeyer
You are assuming the use of exceptions is an evolution in the first place, but
that's far from being a consensus. To some of us, exceptions are very
convenient, but more easily lead to brittle software.

I wrote a little bit about that before. Probably won't help you much, except
perhaps in acknowledging that there's a different angle to that which some
people may care about.

[http://blog.labix.org/2013/04/23/exceptional-
crashes](http://blog.labix.org/2013/04/23/exceptional-crashes)

~~~
sergiosgc
Exceptions are, when you boil it down to the core, a default behavior for
error conditions. It states that, on error, execution jumps to the first point
in the code path expecting the error. If no such point exists, execution
halts.

This is a stark contrast to the default behavior for error returning, which is
to carry on as if nothing happened. How can "keep calm, carry on" be construed
as better is beyond me.

Now, I know the arguments against exceptions are supported on all kinds of
horror code using exceptions out there. Let me preempt that by stating that
assessing a tool for its wrong uses is not a good evaluation of the tool. A
hammer is not a good screwdriver, there are no news there.

~~~
niemeyer
It's not about horror stories, but about choosing a tool that makes it easier
to get the error handling path right, which is just as real and important as
the success case. In my experience with the medium sized projects I've been
closely responsible for (in the hundreds of thousands of lines ballpark), we
were interested in doing a really good job in both the exception and the error
result cases, because the misbehavior scenario is a real issue, and followed
all the good practices in the modern developer's backpack. In the end,
exception-rich code just turned out obviously hard to get right, no matter how
much we _wanted_ to get it right, because the program is allowed to jump out
of arbitrary stack frames that the developer was conveniently taught to not
handle errors on, because.. hey! the "default behavior"!

After getting involved in these experiments, I started paying more attention
to how people can possibly be happy with exception-rich logic that behaves
like that. My empiric observation is that the lack of proper error handling
turns out to have a relatively low impact for lots of projects. Crashed or
misbehaved? Whatever.. file a bug.

------
micro_cam
So I do a lot of scientific programing in go [1]. I am absolutely still using
python + numpy/scipy/pyamg for linear algebra heavy stuff but this has more to
do with the available and well documented packages then operator overloading.

I actually find that the interfaces used for math [2] encourage a more memory
efficient coding style. Without operator overloading I'm less tempted to jam
everything into a pretty one liner and more likely to set things up to work
with out unintended reallocations.

Lack of efficient generics is a much bigger annoyance.

[1] See for example my random forest package
[https://github.com/ryanbressler/CloudForest](https://github.com/ryanbressler/CloudForest)
[2] See package big for example
[http://golang.org/pkg/math/big/](http://golang.org/pkg/math/big/)

------
gbog
When I read go specs or go vs python comparison, nearly every diff is a
specific pain point of our big python code base.

No keyword argument? to hell with them. Strict formatting? That should be in
python interpreter. No unused import: would have saved our lives. No cyclic
dependencies? No inheritance? No exceptions? Great, all of them are
maintenance nightmares.

~~~
smnrchrds
Can you please elaborate. I don't understand what could be wrong with keyword
arguments. Also I can't see how an unused import can waste a life, unless
there is some lethal side-effect to importing that package (I don't like
import side-effects, but they are inconvenient whether the import is used or
unused). Exceptions seem pretty useful as well.

~~~
gbog
Python's keyword arguments:

    
    
        def f(a, b=1, c=2):
            pass
    

Now, same call can be written `f(0, 1, 2)` and `f(0, b=2, a=1)`, which makes
it much harder to refactor. For instance, suppose you want to add a non-
keyword argument, you'll have to carefully grep for all calls.

I think functions should be either full keyword arguments and naming them in
calls should be mandatory. Or no keyword arguments at all. The mixing of both
is "convenient" for prototyping, but has a high long-term cost.

Unused imports:

In a module bar, you have `import foo`. How do you know if it is used or not?
You'll have to scan all modules importing bar to check if they import foo.
Granted, the problem is not that import foo is unused, but that it is imported
in bar's namespace.

Exceptions make it hard to follow code paths and easy to hide important
events. The hardest debugs I've endured are because of exceptions. An innocent
looking `except AttributeError` can be hiding a deep issue in some remote
library playing badly with getattr magic.

That said, I love Python, and still think it is the most elegant language. I
will most certainly teach Python to my kid when he'll be old enough. I also
believe it is best suited for a full serie of programming tasks. But, when
code base grow in size and complexity, some of its "tolerance" become a
burden.

------
baldfat
There is no one best language.

Numpy is not something Go is going to do (Correct em if I am wrong). Julia
would be the faster language to switch to, but personally I switched from
Python to R due to the fact that I like languages that are made for what I am
doing. Python always feels second best to whatever I am doing and I have
branched away from Python. So R for statistics is great for me.

~~~
howeman
Besides operator overloading, why can't Go do Numpy?

------
dragonwriter
> No exceptions [...] If I want higher-level error context I need to propagate
> the error upwards, with an if at every function call along the way.

This is flat out false. Go has fairly standard exceptions (panics), though
instead of using try/catch/finally blocks it uses deferred functions that
perform a hybrid of the "catch" (if they use "recover") and "finally"
functionality.

It is a go _convention_ that _library code_ doesn't let panics escape to the
calling code, so if you are going to use panics in your own code, to follow
the convention you should be recovering from them somewhere in the call chain
that you control rather than letting them escape across the public API of your
code. But that's it, they exist, and can be used. So, while there may be some
ground to complain that when calling the standard library you have to handle
error returns rather than catching exceptions (equivalently, "recovering from
panics") -- though, IMO, there is a good reason for this design choice --
there is absolutely no reason, aside from sheer ignorance and not reading even
the most basic Go documentation -- to say that you don't have exceptions that
you can use within your own code and that you have to to error-return handling
at every level in your own code to deal with error propagation. If you make
that complaint, it is clear you don't know what you are talking about.

~~~
rdtsc
> This is flat out false.

So Go has panics and they are _just like exceptions_ in Python? Because to
accuse the writer of being "flat out false" these have to be exactly the same
and he as to be ignorant to not see the obvious.

Let's say you have some step in your function and you might raise an
exception. In Python you'd surround it with try...catch

    
    
      try:
        dosomething()
      catch SomeException:
        handle_exception()
    

Can you do that in Go. Please show how. And don't crate a new function that
that is not what Python does.

> there is absolutely no reason, aside from sheer ignorance and not reading
> even the most basic Go documentation

ignorance? have you read the basic Python documentation, sounds like you
haven't

~~~
dragonwriter
> So Go has panics and they are _just like exceptions_ in Python? Because to
> accuse the writer of being "flat out false" these have to be exactly the
> same

No, they don't. Because the claim that I called flat out false was this

>> No exceptions [...] If I want higher-level error context I need to
propagate the error upwards, with an if at every function call along the way.

So, no, Go's exception mechanism doesn't have to be "just like Pythons" for
this to be flat-out false. What it does need to be is _anything that allows
propagating errors up a call chain without an if at every function call along
the way_. Which panic/defer/recover _is_.

> And don't crate a new function that that is not what Python does.

Even if I did accept your "must work like Python" standard (which is, itself,
wrong because its not necessary for the statement at issue to be flat-out
false), what's the substantive difference between an immediately-called
anonymous function and a block?

Sure, you could legitimately complain that the example would be more _verbose_
in Go, but that's not the complaint OP made. (The most direct translation of
your example would look something like this in Go -- though in practice you
wouldn't really do this this way):

    
    
      func() { 
        defer func() { 
          r = recover()
          if e, ok := r.(SomeException); ok {
            handle_exception()
          } else {
            panic(e)
          }
        }
        dosomething() 
      }()

------
howeman
As of at least a year ago, their are bindings for the BLAS library of your
choice (github.com/gonum/blas), which are used in many parts of the matrix
package (github.com/gonum/matrix/mat64). As of a week ago, there are bindings
for the Lapack implementation of your choice (github.com/gonum/lapack). These
have not yet been worked in with matrix, but will be eventually (PR welcome!).

~~~
howeman
Not to mention there's 2/3rds of a go BLAS implementation (benchmarks
[https://groups.google.com/forum/#!msg/gonum-
dev/Cqa41tbUUCw/...](https://groups.google.com/forum/#!msg/gonum-
dev/Cqa41tbUUCw/EuTBQFBhod0J)). Level 3 routines are much harder to make
efficient (people are still researching efficient matrix multiply)

------
Goranek
In the beginning i missed repl, but i've realised using repl in the first
place was a mistake.. Now i rely on docs(godoc is awesome) and when i need to
test something i use go playground.

~~~
yonaguska
Could you elaborate on why using the repl was a mistake? I don't really miss
python's repl, but coming from the Lisp repl, the playground doesn't seem to
cut it. Now, if anyone has written an emacs mode that lets you interact with
the playground by loading a buffer- I think I'd be much happier.

~~~
Rapzid
The repl/insta-repl in LightTable was amazing to me. Particularly what you
could do with clojure-script when it was hooked up to the browser.

------
ant_sz
As for the GUI problem mentioned in the article, we now have
[https://github.com/andlabs/ui](https://github.com/andlabs/ui)

I haven't yet tried this lib, It may not as good as something as Qt, but I
think it is a good beginning.

~~~
dubcanada
I played around with it, it is nowhere near Qt, not even like 5% close. Qt
isn't just a GUI library, it's everything you need to build a application in a
nice large bundle. But it's a nice start...

However I feel that Go is not really intended to be used for desktop
applications, I would much rather see a Rust GUI library.

------
ChikkaChiChi
The argument "would have switched to X before" seems to not consider the
possibility of new adoptions.

If I didn't need the things that Go provides before, that does not mean I do
not need them today or tomorrow.

------
rogerdpack
Did he say that people looking for faster build times than C++ went to java?
They may not have found what they were looking for, in that case...

Go does lack a quality [IMO] IDE [re: REPL use is for development], but that
can still come with time.

Quality GUI bindings can also come with time [how often do you actually use
Python for GUI stuff, though...but still nice to have, just not its major use
I doubt]

Another thing you'll miss from Python is accidentally typo'ing variable names
and having to discover that only at runtime :)

Just my rebuttal :)

~~~
pjc50
Java is faster to build than C++ almost all of the time. Header files
(especially with templates) tend to turn C++ in to an O(n^2) run time, often
running into hours.

~~~
rogerdpack
OK, the only thing I can compare it against is my current "java" build times,
and I can tell you that using maven is somewhat slow. So I guess you could say
that if people went to java from c++ for the improved build times, they might
still be interested in Go for the same improvement over java :)

------
higherpurpose
He forgot the part about Go also being a great replacement for Java, for those
who don't like Java.

------
mimighost
Go could take a bite from Python in web development area, but scientific
computing...No chance

------
pothibo
Most of the GUIs are done on a main thread which makes some of Go's advantages
unneeded. Don't build GUI in Go, ever.

Error handling is a way of thinking: TDD is one, Go's approach is another.

REPL is not a must, environment without REPL means you have to think more
about what you want. It's a process. You should be able to read source code
and figure out how things work.

Like many mentioned here, Go is great at doing system stuff, like background
queues and processing. Stay with Python until you need to get thing faster,
then move them to Go. That's how you should think of it.

