3 ms·
Excellent writing; it really makes me want to pick up some Erlang at some point. It's also giving me some interesting ideas in regards to the language I'm curre
by thinkpad20 12y ago
Excellent writing; it really makes me want to pick up some Erlang at some point. It's also giving me some interesting ideas in regards to the language I'm currently working on.
I'll also echo a sentiment brought up by another poster which is that the Go style of error handling seems really unattractive. I'm not sure I'm totally on-board with the Erlang style of "just let it die" (although it seems to work great for Erlang), but being forced to deal with exceptions right away in all cases seems like it would clutter your code and cause a lot of headache. I'm also curious as to what happens if you neglect to handle the error? If you wrote something like
file, err := os.Open("file.go")
return file.Read()
What would happen if an error occurred? Would `file` be nil? If so, wouldn't that easily propagate up, even to functions which, on their surface, shouldn't be expected to fail?
- lostcolony 12y agoIs this a question on Go's error handling (which I know nothing about), or Erlang's (which I am moderately proficient in)?
- a_bonobo 12y agoFirst of all, that wouldn't work in go, it would complain that you don't use the err variable, you could change it to: file, _ := os.Open("file.go") The underscore tells the compiler to ignore the error. And yes, file is <nil> now! As a sidenote, it would be more idiomatic to do this: if _, err := os.Open("file.go"); err != nil { //handle error } // Do stuff with file This has bitten me before - if you use a non-existent key on a map of strings (or dictionary, or hash, however you call it) you get "" and an error back. my_hash := make(map[string]string) value, ok := my_hash["no_key"] value is "", ok is 'false'. What type you get back exactly from a bad request depends on the type of call, but you always get an error back! Maps storing ints return 0, maps storing strings return "", etc. Go always forces you to look at each error, since errors are always different.
- jamwt 12y agofile, err := os.Open("file1") // complains if you don't check err file2, err := os.Open("file2") // no problem, no need to check err.
- signa11 12y agoif _, err := os.Open("file.go"); err != nil { //handle error } // Do stuff with file but you need to split that into 2 lines, just like the good article says i.e. file, err := os.Open("abcd") if err != nil { // <handle os.Open(...) errors } otherwise, outside the 'if {...}' block, 'file' is undefined.
- jerf 12y agoErlang's "let it die" philosophy is surrounded by other support structures that make it work. It works better in that language than it would in most others. I do find that my production-quality Erlang code does somewhat often have to catch errors so it can properly log them, though the relatively recent addition of line numbers is a big help there. (Prior to that, you could end up with a "badmatch" identified only by what function it occurred in. This is not necessarily a problem, but when you see dozens of them per minute filling up your log it made it tricky to figure out what was really going wrong so you could fix it. Even with line numbers it's not always quite enough, though.) In Go, for that code snippet you'd be looking at one of two possibilities, looking at it a bit more generalized than just the specific Open call since I think this is a philosophical question rather than a literal "what does this code literally do?" question. Either "file" would come back nil, in which case the return line would crash with a nil access, or you'd get something that would fail to work correctly. Probably the zero value for the type, but only probably; it would be syntactically legal for the function to half-construct a value and end up returning it to you, expecting you to pick up the err value and know not to use it. I actually don't know what the common practice is here... obviously when I put it that way, returning a half-constructed object is not a good idea, but I don't know in practice whether many things do that, because you always should deal with the error. There is at least one call in the shipped library that can return both an error and a result, which is the Reader interface's Read call: http://golang.org/pkg/io/#Reader http://golang.org/pkg/io/#Reader It is legal (though not mandatory) to both return that some bytes were read, and that we've hit the EOF. (It is mandatory that the next call will return only EOF as the error in that case.) While Erlang and Go are at the opposite ends of the spectrum on error handling, and I think it's easy for either side to look at the other and think it's crazy, but I also think they're both solid ways of operating; in practice I find I produce similar levels of reliability with similar amounts of cognitive effort in both regimes. It's one of those cases where the extremes are viable, and it's the options in the middle that suck. In Erlang, you don't worry much about errors, the system is built around that, and the fact that "=" is not assignment but pattern-match-and-bind makes: {ok, Value} = complicated_function() into an efficient assertion that the function correctly completed and the correct thing is in Value xor I crashed. It makes a naive (in the computer science sense, not the insulting one), "sweep it under the rug" approach to errors do what it should. This is a huge positive... making simple code that anyone can write do the right thing is a very powerful approach, and failing to consider this is one of the Great Software Engineering Sins, in my opinion. On the Go side, it mostly sticks errors in your face and makes damn sure you do something sensible with them. It's not perfect (it's possible to ignore an err... "someday" I'd like to build a go vet extension to help check for this), but it mostly doesn't let you ignore errors and write idiomatic code. Sure, it's more code than it needs to be, but, then, Erlang is not exactly a master of concision either (the number of multi-ugly-line Erlang functions that I could collapse down to one line of Haskell, sigh). This also works. Partisans can have fruitful debates about the pros and cons of each. What doesn't work is making it so that naively-written code ends up completely ignoring errors and just then just skipping dealing with them. That said, honestly, nowadays I can only really think of C that still works that way, though. Most everything else has some solution to this problem (exceptions, mostly).
- mamcx 12y agoI have imagine (for my toy language) that is weird to force one way or another (always handle or always check), but instead use the unix idea of STDOUT/STDERR. A function return values in STDOUT, and exceptions always go to STDERR: This is GO style: file | maybeError? := open('thisnotexist.ever') And is totally ok to say, python style: file := open('thisnotexist.ever') and upper in the chain is possible to catch it: match MyAwesomeFunction(): StdIn: StdErr: Or maybe try/catch. The idea is that if stderr is consumed is handled right there, but if not it "throw" and work like a exception.