Live data from Hacker News

Why I’m not leaving Python for Go

uberpython.wordpress.com

161–170 of 245 posts

Re: Why I’m not leaving Python for Go

#161

My opinion: I'm not a fan of Python. * It's really slow. * Most people write their code statically, but it's dynamically typed language anyway. * Things like '__init__'... what? * Having something you can see (whitespace) be significant is a bad idea. * Even worse, the community likes to use spaces instead of tabs, which makes the 'significant whitespace' a significant problem. * With the exception of Flask, importan…

Sure, python is slow. For problems where this matters, there are usually python libraries available that will do it much faster than pure python (e.g. for scientific number crunching you use numpy, which is using FORTRAN behind the scenes). There are probably some problems where performance is critical and no libraries are available, but not many.

Sure, python is dynamic; at the time it was first created it was hard to provide the level of flexible ("duck") typing python has in a static environment. Now that typing has advanced, we are starting to see some level of type annotation support in python; I would expect it to get static typing eventually, but we can't break existing code. Still, I can totally understand leaving python for a static language with good type inference, like haskell or scala. I don't think go's type system is powerful or elegant enough though.

__init__ is fantastic; it removes all the special cases around constructors you see in other languages, and makes it "just another method" that follows all the usual method resolution rules etc. This means less to learn, less to bite you, and much easier to e.g. refactor a constructor into a factory method (or vice versa).

Whitespace isn't significant, indentation is. And when you look at a piece of code, you can see the indentation far more quickly and easily than you can see the braces. At least I can.

DRY, SOC, SRP are very much part of the python philosophy. I find python's standard library is much better at following "tell, don't ask" than most languages'. I agree that many python programs should follow the DI principle better and possibly the language should have better support for it, but IME the java/spring-approach as it's usually implemented doesn't really provide DI, it's just a complex reimplementation of singletons. I've yet to see a language or framework that can really help programmers do DI right if they don't already know.

Re: Why I’m not leaving Python for Go

#162
post #120

I have mixed feelings about errors as return codes. Then again, I have mixed feelings about exceptions. There are two general use cases for exceptions: 1. Unexpected (typically fatal) problems; 2. As an alternative to multiple return values. (1) is things like out of memory errors. (2) is things like you're trying to parse a user input into a number and it fails. I despise (2) for exceptions. It means writing code li…

Ad your last Python example - the preferred method of doing resource cleanup in Python is the 'with' mechanism: with open("x.txt") as f: #do stuff with f That's it. Obviously, compared to 'defer', it hides stuff - the actual magic happens in special methods __enter__ and __exit__ of the object passed to 'with'.

Ruby by convention uses blocks for the same situation:

    def withSomeResource
        handle = acquireSomeResource
        yield handle
    ensure
        freeSomeResource(handle) if handle
    end
    
    withSomeResource do |h|
        h.whatever
    end
File.open and other parts of the standard library does that by default. I imagine the Python implementation of "with" is pretty similar, given that it can be implemented pretty much like this in Ruby, and "(begin) .. ensure .." is pretty much equivalent to "try ... finally ..":

    def with r
        r.__enter__
        yield(r)
    ensure
        r.__exit__
    end

Re: Why I’m not leaving Python for Go

#163
post #151

Earlier quoted context omitted.

I agree that exceptions can be cumbersome at times. And I do prefer the Go parsing float example to the try/catch one if my entire program is only one line. But in order to live in peace you have to either: * Force every programmer to always check all the error values. or * Allow errors to pass silently. So either you accept Go as a heavy-duty, heavy boilerplate error handling laden language. Or you accept it as a fl…

So, I rarely see eg Java code that actually handles the exceptions it receives. Usually its just "Oh, I got an exception, so I'll print an error (or silently fail) and blindly keep going". Given this, I don't see how Go is any worse with respect to sloppy programmers. They'll Always Find A Way. Also, why couldn't your second example be nested? It seems like either of those two examples could be structured identically…

    Errors should never pass silently
    Unless explicitly silenced
The problem with go is that you have 3 error modes:

1. Explicitly handle everything

2. Implicitly silence sometimes

3. Panic/recover explodes sometimes (who knows where/when?)

Mode #2 is dangerous.

Java/Python give you 2 only modes:

1. Implicitly explode on error.

2. Explicitly silence sometimes.

Where both modes aren't inherently dangerous, i.e. they won't directly cause undefined states to execute.

--------------------

Concerning the nesting in my examples - I'm used to the style of C programming where failing is mostly handled by a return as to keep the program as readable (and thus flat) as possible. So pardon my french but I assumed "// do something" would somehow prevent further usage of 'f'.

Note that the Go example, although tedious, isn't bad in cases where you really do need to check every single possible error.

Re: Why I’m not leaving Python for Go

#164
post #138

Earlier quoted context omitted.

I love Haskell too, but I always run into problems with distributing my compiled haskell binaries to other systems that don't have a GHC compiler available to them. For example, if I compile a binary on my Ubuntu machine and SCP it to a server running centOS Linux, the binary just fails to run because of shared lib issues. Even if you try and compile the binary statically I still run into similar problems. This is in…

That'd kind of defeat the whole point of linking a binary. Were you using cabal to build executables? What shared libs were missing on remote machines?

When I just do a simple hello world app, I get this error

  ghc helloworld.hs -o helloworld
  
  ./helloworld
  ./helloworld: error while loading shared libraries: libgmp.so.10: cannot open shared object file: No such file or directory
However if I compile statically, I get a different error

  ghc helloworld.hs --make -optc-static -optl-static -optl-pthread -static -o helloworld
  
  ./helloworld
  FATAL: kernel too old 
  Segmentation fault
This guy experiences similar issues.

http://gaiustech.wordpress.com/2010/09/03/on-deployment/

Re: Why I’m not leaving Python for Go

#165
post #53

If you use a language that uses error codes, then you're forced to explicitly think about what to do in every single error case (or not, and accept the consequences). This can add a lot of mental overhead, but can result in a much more robust and well-thought-out program. But because more logic is involved period (to handle the robustness), the program is necessarily more complex. If you use exception handling, then…

I would guess that most (99%) of C programmers are lazy or has an rapid programming style then. Its a rare thing to see C source code that even do the simple thing like wrapping every write/read/print call with a loop that detects eintr. In my years of programming, I have yet to stumble on a other programer who even knows that one should be doing this. Looking at c libraries and their example code, almost every time,…

EINTR is a flaw in Unix's syscall model: it forces on every single application the responsibility for dealing with something that should have been handled at a lower level. That's unrelated to C's error handling style; it would be just as ugly if every write() call had to be wrapped in a try-catch block to retry when an InterruptedException was thrown, and people would still forget to write that ugly boilerplate.

Re: Why I’m not leaving Python for Go

#166

Earlier quoted context omitted.

This kind of comment kind of misses the point. The monadic style of threading error values is perfectly compatible with imperative programming if only language designers knew about it.

Problem is that you then have to explain monads to average programmers who'll be using the language. I love monads (I love arrows more, but that's another issue). I'm a language design geek. My level of expertise is different from someone who has just been hired into a new job and wants to get things done. I suspect that most language designers know about monads - I'm not sure Rob Pike did, but it's pretty common kno…

>Problem is that you then have to explain monads to average programmers who'll be using the language.

No you don't, this is exactly what you don't need to do. Do we explain the finer points of stream implementation to would-be C++ programmers? No, when they need to do something non-standard with a stream/monad, that's the time to talk about them. For the majority of programmers it's just "when you need to write to a file you do it like this".

Re: Why I’m not leaving Python for Go

#167
post #162

Earlier quoted context omitted.

Ad your last Python example - the preferred method of doing resource cleanup in Python is the 'with' mechanism: with open("x.txt") as f: #do stuff with f That's it. Obviously, compared to 'defer', it hides stuff - the actual magic happens in special methods __enter__ and __exit__ of the object passed to 'with'.

Ruby by convention uses blocks for the same situation: def withSomeResource handle = acquireSomeResource yield handle ensure freeSomeResource(handle) if handle end withSomeResource do |h| h.whatever end File.open and other parts of the standard library does that by default. I imagine the Python implementation of "with" is pretty similar, given that it can be implemented pretty much like this in Ruby, and "(begin) ..…

The two mechanisms are not very similar. With the blocks mechanism (if I understand correctly) the resource itself implements a method "execute this code and clean up yourself". This only uses the standard mechanisms of the language (i.e. blocks.) The control is with the resource.

Python's 'with' inverts the control - the control is with the language runtime and the resource is passive here. In fact 'with' is a special syntax extension, with calls to hard coded method names (__enter__ and __exit__) done by the Python runtime. The resource itself (or the contextmanager representing it) only implements __enter__ and __exit__ and is passive with regards to this mechanism.

Re: Why I’m not leaving Python for Go

#168
post #120

I have mixed feelings about errors as return codes. Then again, I have mixed feelings about exceptions. There are two general use cases for exceptions: 1. Unexpected (typically fatal) problems; 2. As an alternative to multiple return values. (1) is things like out of memory errors. (2) is things like you're trying to parse a user input into a number and it fails. I despise (2) for exceptions. It means writing code li…

>try: >> f = float(someText) >> catch ValueError: >> # I just parsed you, this is crazy, >> # here's an exception, throw it maybe?

No, you have it exactly backwards. It's with return values that you have to check after every call to be safe. With exceptions you can truly say: if I don't know what to do if this fails then I don't do anything. You can let the exception bubble up higher, all the way up to crashing the program if you like (which gives a nice stack trace that can then be debugged).

With return values, if you don't check then you could be building up more and more trash and you won't even know it until you either hit a point of code that finally does look at what's returned, or if an exception occurs (e.g. segfault).

Re: Why I’m not leaving Python for Go

#169
post #73
post #61

Earlier quoted context omitted.

Um... errors ARE normal operation. Consult historical output from your C++ compiler, if you don't believe me! (If anything, that should probably be "errors", quotes included, because what people usually mean by the term is "easily-forseeable occurrence that I couldn't be bothered to write the code for".)

Compiler errors aren't usually implemented as exceptions because compilers these days don't stop at the first error. Exceptions are ideal for things that require aborting and unwinding to a point - usually a loop, like a server request handler or UI event dispatcher - high up on the stack. If the general behaviour is not to abort and unwind, it's not a good fit for an exception. It's also why exception handlers shoul…

My point was that programmers are happy to push the "error" handling off to some other part of the code, far away from the point at which the "error" arises, in some place that usually has no real idea how to handle it, or present it, or what have you. All as if there's some default privileged path where everything is running normally, and then this occasional strange special case that crops up now and again. Exceptions explicitly encourage this style of programming - keeping the error handling apart from the main logic is their very purpose.

But were programmers to consult the history of their compiler's output (representative summary: "ERROR ... ERROR ... FAILED ... WARNING ... ERROR"), it would be obvious that in fact errors are highly likely, even if you know what you're doing. So what makes the error case different from all the other cases?

Re: Why I’m not leaving Python for Go

#170
Personally I think the community might be spending too much time on Python 3.

Python 2 is fine, and the upgrade will offer few significant advantages for most people.

I think the effort would be better spent working on things like frameworks and tools for web development.

Post reply on HN