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.
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!