In which I learn about metamethods, try my best to restrain myself from abusing them, and get an opportunity to hurl more abuse at Java
I fancied learning a new language, and needed an excuse to do it. Fortunately, I have been assigned to create a raytracer. We'd already discussed the mathematics of it in class, and the end result is fairly obvious, so there shouldn't be too much frustration caused by boxing myself into a design corner accidentally. What better opportunity?
There were two languages hovering in my mind, Lua and Scala. Well, raytracers need performance, and LuaJIT was far more promising in this respect than Scala, so the choice was obvious. It turns out Lua is an amazingly simple language - for something that isn't Lisp or Brainfuck, at any rate.
This also means there isn't any built-in object system, should you want one. Strings are strings, functions are first-class (yay!), integers are doubles (that's much better than it sounds, by the way) and tables are the only data structure you get to play with. Tables are arrays and/or dictionaries, and while the way you access array and dictionary elements are the same - var[x] - they exist independently, for the most part, so your fears of hash-table-backed arrays are unfounded.
You also get to access dictionary elements using var.element, which is equivalent to var["element"] - a nice bit of syntactic sugar which JavaScript also has, and helps you feel as if you're dealing with well-defined objects rather than key/value stores. You can also duplicate JavaScript's prototype-oriented system very easily in Lua (more later). However, that's where the similarity ends.
Along with tables, you also get metatables. They let you customise the behaviour of a table, remapping operations upon it with your own functions. Think operator overloading in C++, except in a dynamic language. Importantly, you get to overload key accesses and writes, though only for keys that don't exist in the table inspected (for performance reasons if anything else). For example, we try to access foo["abc"], but foo doesn't contain abc. So, we call foo's metamethod for looking up table keys, which simply returns the value of the key in another table, bar. Ta-da, you've just inherited the keys of bar for free! You can do more complicated things, if you wish - but use your power with restraint.
I've seen far too many people condemn operator overloading in C++ - and often for good reason, since there are many things to consider, and many assumptions must be made such as the burden of object allocation, exceptions, and dealing with the sometimes-baroque overload resolution rules.
In Lua, however, you have garbage collection, a well-defined exception system (die horribly), and the simple rule of 'call the left object's metamethod, if it doesn't have one, call the right object's metamethod, if that doesn't have one then die horribly'. It's not complicated, and it leaves you to do something sensible, whatever that might mean in your case.
On the subject of exceptions, this isn't like Java where you are forced to catch exceptions for things which are honestly rather trivial, like a NumberFormatException for parsing a bloody integer of all things. Lua's error system is borne from its origins and primary purpose as an extension language, in that it's OK for a chunk of code to error and return control to its caller, since you probably won't have huge chunks of code anyway, and errors worth halting the whole thing are due to faulty program logic rather than edge cases. As a result, Lua and its libraries return error codes for things which aren't the programmer's fault, such as trying and failing to open a file, and raises halting errors for things that are, like accessing a non-existent member of a table. This is further encouraged because the only error control mechanisms you get are error (and assert) for raising errors, and pcall to execute a function and capture either its return value or its error.
It's certainly satisfactory enough for most uses, though I'd rather have Python's more powerful exception handling if I was writing a large program. In any case, error handling depends more on the programmer than the language. Even when it tries to force your hand like Java's checked exceptions does, it's no guarantee you actually solve the problem, let alone in a reasonable way.
There's one thing Lua does better than Python, though (and Java, but that's obvious). First-class functions with real closures. The best way I can express it is being able to pass around behaviour with state as an object like any other, and being able to define it with the same convenience. If something is easy to use, you will use it more often. Being able to define behaviour means you'll readily create functions that accept behaviours, just like map, filter, sort... and thus get one step closer to the Holy Grail of Code Reuse. I think we can all agree that the less new stuff we have to write, the easier it is for us and the fewer new bugs we will introduce. Amen to that.










