top | item 2977449

Comparing Go with Lua

64 points| mapleoin | 14 years ago |steved-imaginaryreal.blogspot.com

14 comments

order
[+] davidhollander|14 years ago|reply
I'm a big fan of Lua and Go's use of multiple return values, nil, and simplified error handling, (No exception 'types'). I think the problem with 'typing' Exceptions is that it means you are using it to record and pass describable\known state similar to return, instead of being reserved for unknown state. For indicating predictable failure states without using exceptions, I think multiple return values as used in Go and Lua is a much better paradigm. To recap Steve's post, opening a non-existent file in Lua:

    =io.open 'sdfasdf'
    nil	sdfasdf: No such file or directory	2
The first value returned is nil, the 2nd value is a string containing an error message, and the 3rd is an integer error code. This allows you to write most code using 2 state boolean logic:

    f=io.open 'sasdasdfsf'
    if f then -- nil is a boolean 0
whereas in Python, one often has to reason about 3 states due to the use of try\except blocks. Even though you are not using typed Exceptions, multiple return values does not discard any state about the error if it is desired:

    f, err = io.open 'sasdasd'
    print(err)
In Lua (not Go), when you combine multiple return values with an assert() function of the form assert(boolean, error_message), you effectively get a mechanism for making exceptions optional:

    > =assert(io.open 'sdfasdf')
    stdin:1: sdfasdf: No such file or directory
    stack traceback:
    	[C]: in function 'assert'
    	stdin:1: in main chunk
    	[C]: ?
Although Go does not appear to include assertions at all, arguing against them in its FAQ: http://golang.org/doc/go_faq.html#assertions
[+] Flow|14 years ago|reply
The idea of exceptions is not primarily reading out details from the throw exception object, but to let programmers code the "happy flow" as if errors can not happen. It also makes it easier to let callers further up in the call-chain to set the policy of of what to do in case of an error.
[+] danssig|14 years ago|reply
IMO going back to return statements for error detection is to throw away years of advancement. The problem with what you're describing is now every call has to be tested for errors because only the call-site has a chance to do this.

Contrast this with exceptions: if I don't know what to do with a failure at this level then I just ignore it. Some caller higher up the chain will know and can handle it.

Your examples seem overly trivial. In real code it would be very rare for the level of code that's actually opening files (something far down in the libraries) to also be handling exceptions.

[+] seri|14 years ago|reply
I independently ended up with the same impression with the OP. Go feels just like a statically typed Lua. Not only does Go inherits Lua in some specific language features as pointed out by the OP, but it also learns from Lua's design philosophy: be minimal.

When I was going through my journey with Go discovery, I noticed that the Go authors obsessively tried to keep the syntax size small. There is only one looping construct. The only polymorphism mechanism is interface, but interfaces are both very simple and flexible. This is like what Lua does with tables.

This also means that when a feature introduces complexity into the language, the Go authors choose to discard it. There are advantages and disadvantages with exceptions, but I think what ultimately motivated the Go authors to leave exceptions behind is that it may complicate things. And Go also doesn't have any kind of compile-time generic, so one can't write functions like append() and copy() in pure Go.

[+] masklinn|14 years ago|reply
> But the big similarity is this: functions can return more than one value. This is quite distinct from returning tuples, as Python does and is very much more efficient. (Bruce Eckel errs in speaking of tuples in Go) So the following style is common: a function returning a value plus an optional error:

I have trouble with this assertion: as far as I can tell, nothing would stop a language (especially a statically typed one) from optimizing a tuple return/unpack into a cheaper parallel assignment, and still provide a full-blown tuple type. I might be wrong, but is tuples unpacking not a strict superset of Go/Lua's restricted multiple return values?

(this is not possible in Python, because Python really unpacks arbitrary iterables, and Python 3 introduces ruby-style unpacking slices, so you can't apply such an optimization unless you can infer the exact return type of the callee)

Multiple return values is not intrinsically more efficient than tuples unpacking, it's just foisting an optimization onto the language user (by arbitrarily limiting his options in this case).

Even the following pattern of unpacking into a function arguments can trivially be handled via tuples unpacking (by making them explicit, which counts as a positive as far as I'm concerned)

> Go and Lua provide mechanisms for protected calls which are similar in spirit. Something bad will happen in a function, and the idea is to prevent that badness from bringing down the process.

a.k.a. exception handlers by any other name.

> The strength of the Go approach is that there is an explicit mechanism for calling arbitrary code when a function stack frame goes out of scope. With Lua (as with most garbage-collected languages) the best you can do is define finalizers on objects and wait for the garbage collector to call them (eventually.)

Right, because there's no such thing as `BlockClosure#ensure:`, `unwind-protect`, `using()` or `with:`.

> The difference is that with Go you have to use an underscore if you want to ignore the value (it is very strict about unused variables)

The issue with a bare `_` is that it has limited readability. Sometimes, you want to say "This is that, but I don't care for it". Erlang lets you do that by `_` being used as a prefix as well as a wildcard (technically I believe it introduces bindings, but more interestingly it makes the compiler not warn about unused bindings). It is very useful for bit positional matches of which half are thrown out.

> Note also the similar for-iterator syntax which naturally flows out of multiple return values.

Which you can just as easily get from tuples unpacking, of course.

> Lua's multiple returns came from CLU according to The Evolution of Lua. It is interesting how long it can take for new programming language concepts to reach the mainstream.

About 17 years[0] is not that much, and that's only if you discount pattern-matched functional languages[1].

[0] http://docs.python.org/reference/simple_stmts.html#assignmen...

[1] http://en.wikipedia.org/wiki/ML_(programming_language)

[+] pcwalton|14 years ago|reply
"I have trouble with this assertion: as far as I can tell, nothing would stop a language (especially a statically typed one) from optimizing a tuple return/unpack into a cheaper parallel assignment, and still provide a full-blown tuple type. I might be wrong, but is tuples unpacking not a strict superset of Go/Lua's restricted multiple return values?"

Well, it depends on how you implement polymorphic code, but generally yes. This is why we use tuples instead of multiple return values in Rust. In fact, if you return a struct by value in LLVM, LLVM will allocate a virtual register for each element.

[+] georgieporgie|14 years ago|reply
It would be nice if the code samples were colored or tagged according to language, since I know neither Go nor Lua.