Hacker News new | ask | show | jobs
by groovy2shoes 5258 days ago
Good article, but one nitpick: not everything in Lua is a table. Tables are a versatile data structure that can be used as arrays, dictionaries, objects, etc. However, Lua provides many types besides tables: numbers, booleans, closures (functions), strings (which are interned), coroutines, and something called userdata (which are typically pointers to something implemented in C).

Another cool thing about Lua, which was mentioned only briefly in the article, is proper tail-call optimization (TCO) like you'd find in Scheme. TCO makes expressing recursive algorithms nicer because you don't have to worry about blowing your stack.

Lua's design philosophy -- giving the programmer a handful of basic yet powerful features -- makes it somewhat Schemy. I suspect that Lua is about as close as you can get to a ball of mud before becoming Lisp.

3 comments

To nitpick your nitpick: in Lua tables are the only composite data structure. Everything you listed is either atomic or opaque (you can hide composite datatypes in userdata, but you can't introspect the values).

If you think about it, this is very similar to C where the only composite datatype is a struct (arrays are just sugar on pointer arithmetic). In fact, I think if you wanted to make a scriptable dialect of C, you'd create Lua.

Of course, by being simple, add in dynamic typing and first-class functions, and Lua does feel a bit like Scheme's kid brother. Or, rather, Lua (in my mind) reveals how C and Scheme aren't so far apart after all!

Well, since we are nitpicking nitpicks, allow me to nitpick your nitpick of a nitpick: Lua closures are also composite data structures. You can get/set their upvalues via the debug API. Behold:

  function create()
    local a,b,c,d,e,f,g,h,i,j;
    return function() print(a,b,c,d,e,f,g,h,i,j); end
  end
  function array_get(f, index)
    local k,v = debug.getupvalue(f, index);
    return v;
  end
  function array_set(f, index, value)
    debug.setupvalue(f, index, value);
  end
  function map_get(f, key)
    for i=1,math.huge do
      local k,v = debug.getupvalue(f, i);
      if k == key then return v; end
      if k == nil then return nil; end
    end
  end
  function map_set(f, key, value)
    for i=1,math.huge do
      local k,v = debug.getupvalue(f, i);
      if k == nil then return; end
      if k == key then
        debug.setupvalue(f, i, value);
        return;
      end
    end
  end
  
  function test()
    local f = create();
    array_set(f, 1, "one")
    assert(array_get(f, 1) == "one");
    map_set(f, "b", "something");
    assert(map_get(f, "b") == "something");
    print("Success!")
  end
  test();
If table creation was disallowed, you could use functions in place of tables. And you could repurpose an existing table as the function type's metatable, allowing syntax like func.x=func[y]

Oh, and coroutines probably also fall under composite data structures technically...

Very well played! If I were the nitpick-y type ;) I'd wonder whether use of the debug library is kosher (esp. in production code), but you are definitely correct. Though, actually, this again correlates very closely with C/Scheme -- You can use an offset from a stack frame pointer to access stack variables in a function scope in C; and upvalues are just closed-over variables a la Scheme closures.

Actually, that's another point in Lua's favor. The mechanism Lua uses for managing closed-over variables is WAY more elegant than, for example, Ruby's mechanism...

Yes, tables are the only composite data type in Lua. But it's not fair to say that everything is a table. Compare with Smalltalk or Ruby where everything is an object, even types that are not usually viewed as composites.
Lua has a lot of depth to explore (TCO, coroutines, prototypes, ...), but the language has been carefully designed to only really be like JSON, if that's all you need. The advanced parts stay out of the way.
Good point. I was thinking (as jballanc sort of mentioned) of this with regards to composite types. E.g., there is no difference between lists versus hashes versus tuples versus objects, etc. But duly noted for any revised edition :)