I know it’s bikeshedding and leads to pointless discussions, but in my opinion Lua would be the perfect scripting language if it had 0-based indexing.

“It makes more sense this way” is not a good enough reason to break convention.

Pardon my ignorance. But in many languages that have alternative ways of iterating you tend to user indexes less and less. Isn't that true in Lua?

Sometimes I feel like I'm the only person in the world who does not care about zero-indexed VS one-indexed. It's just the way Lua is, no big deal. Then again, I don't care about significant whitespace either. Maybe I'm just weird.

There is nothing stopping you from doing someArray[0] = "the first item", you know.

For me, the table is extremely powerful. I like it that it can be used as a sparse array, a hash, a vector, whatever. Of course one must know, at heart, the difference between pairs() and ipairs() and what it means for your data, though ..

> For me, the table is extremely powerful. I like it that it can be used as a sparse array, a hash, a vector, whatever. Of course one must know, at heart, the difference between pairs() and ipairs() and what it means for your data, though ..

So, as someone only very peripherally familiar with Lua, can someone please explain the table thing to me? I've heard Lua fans gush that Lua is tables all the way down, except it seems like there's these tables on the one hand that work like arrays, and those other tables on the other hand that work like objects, and you can't mix them up...

Is that not just an ordinary dynamically typed language with arrays and objects then, except it overloads the word "table" to refer to both?

I'm sure I'm missing something, happy to hear what that is.

It's not really tables all the way down - Lua has datatypes like nil, boolean, number, string, table, userdata + lightuserdata, functions, coroutines. I think that's the entire list, actually.

So a table is a hashtable. Just about anything can be a key to the hash - a number, a string, a boolean, another table, a userdata. I can't recall if functions and coroutines can be keys but I suspect they could. I definitely know that nil can't be an index.

In Lua - all iterators are implemented as a function + state + current key. When you write "for x in (whatever)", that (whatever) is really a function call like func(state,key). Lua calls it repeatedly to traverse the loop.

Lua will take the first returned value and use it as the new key in the next call to func(). Once the function returns nil as the first value (or just returns no values) - the loop stops.

There are two functions - pairs and ipairs - that really just return the built-in next function with a starting key. pairs returns (lua's next(), the table, nil), and ipairs returns (next(), the table, 0).

(there's a bit more to it than that and some under-the-hood changes between Lua versions but we'll just go with that explanation).

Lua repeatedly calls that next() function with the table and previous key.

Say you had an array like table like { [1] = some_value, [2] = another_value }.

When you write something like "for i,v in ipairs(a_table)" that roughly expands to:

  * pairs() returns next(), the table, 0. 
  * Lua calls next(table, 0) and next returns (1, some_value)
  * Lua calls next(table, 1) and next returns (2, another_value)
  * Lua calls next(table, 2) and next returns nil.
So - when is a table an array?

When you can iterate through it with sequential, integer keys.

Note - you don't necessarily need to use ipairs to iterate. Lua also has a numerical "for" loop so you could do that. Or - if you want to start your arrays at 0 instead of one you can write your own ipairs() like function in just a few lines of code. Observe:

  local zpair_it = function(table, key)
    key = key + 1
    if table[key] then
      return key, table[key]
    end
  end

  local zpairs = function(table)
    return zpair_it, table, -1
  end

  local tab = { [0] = 'yay', [1] = 'huzzah' }
  for i,v in zpairs(tab) do
    print(i,v)
  end

There - now instead of using ipairs(), you can use zpairs() with zero-based indexes.

As far as using like objects, that's getting into metatables and stuff but - basically lua has a syntactical sugar to make object-orientation nice.

If you write "obj:func()" - that's the same as writing "obj.func(obj)" - assuming "obj" is a table, Lua will fetch the "func" key from the table, then call it as a function with "obj" as the first argument (this can be referred to as self within your function definition).

If Lua tries to fetch "func" from your table and it doesn't exist - it will check to see if your table has a metatable defined with an __index metamethod (or table) and pull func from that. So - your table could just have your object's actual state data on it, and functions are referenced in a separate metatable.

Observe:

  local dog_methods = {
    bark = function(self)
      print("bark bark I'm a",self.breed)
    end
  }

  local dog_metatable = {
    __index = dog_methods
  }

  local huskie = setmetatable({
    breed = 'huskie'
  }, dog_metatable)

  local collie = setmetatable({
    breed = 'collie'
  }, dog_metatable)

  huskie:bark()
  collie:bark()

huskie:bark() is equivlanet to huskie.bark(huskie). bark doesn't actually exist on huskie, but huskie has a metatable with an __index table where bark is defined. So the function call is really more like dog_methods.bark(huskie).

Anyways wow that was a lot. Highly recommend going through the Lua manual. It's not a long read.

Tables are kinda-sorta hashes that can hold anything, not unlike JavaScript objects. The array use case is just a table with automatically assigned numeric keys.

> There is nothing stopping you from doing someArray[0] = "the first item", you know.

Yes, there is:

    local l = {[0] = 'a', [1] = 'b', [2] = 'c'}
    for i, c in ipairs(l) do
        print(i, c)
    end
This will only print the last two pairs. Lua is 1-indexed, end of story. You can store values at index zero, but it's no different than storing values at index -1 or index 'lolrofl'. It does not exist in the array-part of the table as far as Lua is concerned.

If you're going to use a table as an array, use it as an array:

    local l = {[0] = 'a', [1] = 'b', [2] = 'c'}
    for i = 0,#l do
      print(l[i])
    end

    .. prints:

    a
    b
    c
Lua haters usually don't get past their misunderstanding of tables, but its really quite unfair on the language and those who have used it quite well to do big things ..

I've always assumed that there is some technical reason for Lua being 1 indexed, rather than it being a design choice.

Either way, I think it's a nitpick to complain about. I've written a decent amount of Lua and there's only been a handful of times where 1-indexing was even relevant to me.

It's a design choice. Lua was first intended to be a configuration language for engineers to enter data at an oil company. They were used to the 1st thing being number 1, the 2nd thing being number 2, and so forth. It's just a very natural way of counting.

You don't change something like that because it eventually got picked up by game programmers (never the intent of Lua, something that just happened after it was used by the Grim Fandango team, then it took off in the gaming world).