Live data from Hacker News

Why I’m not leaving Python for Go

uberpython.wordpress.com

181–190 of 245 posts

Re: Why I’m not leaving Python for Go

#181
post #80

As usual in these threads about Go, I really wish people would consider Haskell as a nice alternative. A lot of people write Haskell off as "academic" or "impractical", which I feel is not an entirely fair assessment. Particularly: Haskell is fast, concurrent by design (whatever that means, I'm sure Haskell is :P), typed but not cumbersome or ugly (less cumbersome and ugly than Go's types, even) and--most importantly…

> As usual in these threads about Go, I really wish people would consider Haskell as a nice alternative. A lot of people write Haskell off as "academic" or "impractical", which I feel is not an entirely fair assessment.

As someone with 10+ years of programming experience in the industry but no formal college education, I see the problem with Haskell (and other, similar functional programming languages) that in order to fully understand the language, you need to understand its theoretical foundation. Same goes for monads, combinators, etc. Without understanding the theory they're based on, it's impossible to use them and also hard to read programs that employ them.

The theoretical foundation of Go on the other hand is much smaller, and way easier to understand, especially for people without a formal CS education.

So, yes, I admit that I don't "get" Haskell in all its glory, and I'm not ashamed of it because I know that most people in IT don't and that's why it will always remain relatively obscure even if its approaches to a number of programming language problems are technically and theoretically sound.

Re: Why I’m not leaving Python for Go

#182
post #91
post #53

Earlier quoted context omitted.

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,…

In Go if you use the return value of a function you must also assign the error to something, either a variable or explicitly ignore it by assigning it to _. It was designed this way specifically to avoid the problem of not checking error conditions in C code.

This breaks down when the method you call has no return value other than an error. In that case it is easy to forget.

Re: Why I’m not leaving Python for Go

#183
post #156
post #79

Earlier quoted context omitted.

So instead of writing x, err := something() if err != nil { handleError(err) } n, err := somethingElse(x) if err != nil { handleError(err) } andSoOn() you could write switch x := something() { case error: handleError(err) case string: switch n := somethingElse(x) { case error: handleError(n) case int: andSoOn() } } ?

Yes. The advantage of the second form is that x := something() n := somethingElse(x) andSoOn() no longer compiles. Obviously you would also want some syntactic sugar to make it look nice, but that's quite simple.

There's also the option of doing it on an explicitly tagged union, Erlang-style: `something` returns not `Value` or `Either(Error, Value)` but `{ok, Value} | {error, Reason}`.

This means you can handle the error:

    case something() of
        {ok, Value} -> %%;
        {error, Reason} -> %%
    end
or you can "ignore" it

    {ok, Value} = something()
but (and this is important) the latter *will not pass silently if `something()` returns an error.

Instead, it will raise a "BadMatch" fault, similar to an Haskell-ish

    let (Right value) = something()
where `something :: Either a b`

Re: Why I’m not leaving Python for Go

#184
post #179

Earlier quoted context omitted.

One problem with Go is that is uses multiple return values to indicate errors instead of alternative return values. When you call strconv.ParseFloat, you always get back two values, the error code and... wait, what float do you get back when there's an error? If you're going to use return values to indicate errors (and certainly I feel that there are reasons to use them, exceptions vs error codes is not an either/or…

> One problem with Go is that is uses multiple return values to indicate errors instead of alternative return values. When you call strconv.ParseFloat, you always get back two values, the error code and... wait, what float do you get back when there's an error? Why would you even be interested in the float when there's an error?

That's his point: why do you get something typed as float back when there's an error? And what does that thing mean?

Re: Why I’m not leaving Python for Go

#185
post #89
post #81

Out of curiousity: Aside from "returning errors" and "throwing exceptions", is there any interesting research into other ways of error handling?

Common Lisp has an interesting exception system where errors have a chance to be handled without unwindind the stack. Basically, functions also receive an "error handling" object as an extra argument and they consult when they encounter an exception. The error handling object then edcides wether to continue operation, or to unwind back.

> Common Lisp has an interesting exception system

Called "conditions", fwiw. Smalltalk has a very similar system (the main difference is in the declaration of restarts)

Re: Why I’m not leaving Python for Go

#186
post #66

Earlier quoted context omitted.

I respectfully disagree. You almost never cast the error. Two approaches are common in the go community. 1. Error constants. These you can use == or a switch statement to do control flow with. 2. Custom Error Types. These you typically use a type switch which uses reflection to do dispatch off. In this case you might also then cast it if you need specific data off of the error but most of the time you don't need any…

I respectfully disagree: http://golang.org/src/pkg/net/timeout_test.go#L39 edit: You should note that the above test code is actually far less verbose than the analogous end-user code, given that the timeout_test.go clearly expects the 'err' var to be a net.Error. Now consider the case of a network subsystem, with funcs having in args of type 'bufio.Reader' and/or 'bufio.Writer'. At some prior point you may have set…

You are using an internal testing package as an example?

First you wouldn't cast to net.Error like the test code for real code. That code is testing internal details of the net package and not meant to be a guide for idiomatic consumption of the code at the level you are describing.

here is what you would actually do in production code:

    _, e := readMessage(reader)

    switch et := e.(type) {
    case net.OpError:
       // handle OpErrors specifically
      if et.timeout() { /* handle timeout */ }
    case net.AddrError:
      // handle AddrErrors specifically
    case net.DNSError:
      // handle DNS errors specifically
    case net.Error:
      // handle all the rest of the net packages errors
    default:
      // deal with other errors
    }
Of course that's just for low level code. if you want to see if net.Error has code leaking though the wrapping package then you would perhaps use a case looking for all the wrapper packages specific errors and then look for net.Error types after that.

Most of the time you won't be dealing with the net package either. Instead you will be dealing with the http package for instance where your errors will mostly look like this: http://golang.org/pkg/net/http/#variables for which you can use a regular switch. I have just listed both of my cases taken directly from the stdlib including the case you listed.

Re: Why I’m not leaving Python for Go

#187
post #174

Earlier quoted context omitted.

Amusingly enough, I'm pretty sure "FATAL: kernel too old" is a problem with glibc, so it's actually the C standard library that's preventing you from running your staticly-linked Haskell binary on your server.

So the haskell RTS requires the same version of glibc on both machines to make it work? This wouldn't be too much of a problem but I don't have administrative privileges to these servers. So when I want to write throwaway scripts or programs, I find myself turning to Go or D and they work without any quibbles.

Nope. In theory you'd have exactly the same problem with any statically-linked binaries compiled on that machine and run on that server, regardless of what language they're written in. glibc has a minimum kernel version requirement and the glibc you're statically linking against just plain isn't compatible with the kernel on the machine you're running it on. Dynamically linking to a newer glibc and running against an older one doesn't generally work either, and IIRC may even result in apps that appear to start but crash unexpectedly when they try to access versions of library calls that aren't there, again regardless of language used.

Your problems with the non-statically-linked version, on the other hand, can probably be solved by copying the appropriate libraries to a directory on the server and pointing LD_LIBRARY_PATH at it... at least until you run into glibc problems.

Basically, what you're doing isn't supported and the fact that it worked for you with languages other than Haskell is mostly luck.

Re: Why I’m not leaving Python for Go

#188
post #162

Earlier quoted context omitted.

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 speci…

> With the blocks mechanism (if I understand correctly) the resource itself implements a method "execute this code and clean up yourself".

Where the method is doesn't matter, e.g. the (incomplete) example I gave of implementing "with" in Ruby that shows a method that can be defined in the global "main" scope or wherever you want.

Some classes implement class-methods to instantiate an object and pass it to a black and free the resource as a convenience, such as File.open, but it could go wherever else.

The resource itself most certainly does not need to know a thing about it as long as its API lets you do the cleanup you want/need.

> The control is with the resource.

The control is with whatever calls yield. Whether that be a method on the global "main" object (closest thing Ruby has to a global, freestanding function) or on the class of the resource itself, or an explicit "begin ... ensure ... end" block if you only need it once.

> In fact 'with' is a special syntax extension, with calls to hard coded method names (__enter__ and __exit__) done by the Python runtime.

Which is why I gave my example of how I thought "with" would look like in Ruby (as it turns out I missed some exception handling in order for it to be equivalent to the Python version in functionality).

Re: Why I’m not leaving Python for Go

#189
post #162

Earlier quoted context omitted.

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) ..…

> given that it can be implemented pretty much like this in Ruby, and "(begin) .. ensure .." is pretty much equivalent to "try ... finally ..": Note that there's a difference in the handling of exceptions: if an exception is triggered from the `with` block, it is intercepted, provided to __exit__ and can be silenced if needed or desired (by returning a truthy value). So a closer approximation would be: def with r r._…

Ah. Wasn't aware of the ability to silence the Exceptions. Thanks.

Re: Why I’m not leaving Python for Go

#190
post #172

What happens in Go if there is an exception (division by zero or whatnot)? Does it just exit, no stack trace? I am so used to exceptions that I don't even know how it used to work without them. I think getting a stack trace for debugging is really important...

You get a panic, which can be caught. http://play.golang.org/p/IaTAGizhrF
Post reply on HN