- the biggest impact this has is on rendering code, where you have to subtract 1 to position things relative to the origin - which is at `(0, 0)` (or `(0, 0, 0)` in 3D.)
- it's a tad bit more line noise, but not that terrible. I [did design a language using `do`..`end` blocks][def:mica/repo] and it really doesn't look that bad
- partly because it's so fucking bold I can literally not imagine myself designing a language with a strong distinction between hash tables and arrays, and even tuples and records!
- tables are extremely powerful in what they can do, because they're more than just a way of structuring data - they also allow for interfacing with the language _syntax_ through operator overloading
- the way it works is that [`a.b` is just syntax sugar for `a["b"]`][branch:01HRG2RJC2PA5KE0DH0RRFGW9E], which means you overload `[]` to _fall back to another table_ - and that way you can achieve [prototype-based inheritance](https://en.wikipedia.org/wiki/Prototype-based_programming)!
it's similar to the principles of [Go](https://go.dev/), where the language encourages using dumb constructs rather than super clever code with lots of abstraction.
- though unlike Go, Lua has the goal of being _small_ because it needs to be _embeddable_, especially given it's used in very constrained environments in the real world. (microcontrollers!)
- Lua also knows _very_ well how much syntax sugar to have to make writing code pleasant, but not to overdose it so much as to give you instant diabetes.
+ as an example, there's function call syntax: you can pass it a string or table _literal_, which is just enough to enable some really nice DSLs without making the grammar too complex.
- the only missing thing then would be list comprehensions to be able to transform data into GUI elements, but even that can be ironed over using function literals:
```lua
render {
width = 800, height = 600,
title = "Hello, world!",
vertical_box {
header1 "Hello, world!",
paragraph "This is an example GUI. Here's a horizontal list of numbers:",
- there is also the incredibly useful sugar for indexing tables by string literals: instead of `table["x"]` you can write down `table.x`
% id = "01HRG2RJC2WDXCPW12JDD4749R"
+ and there is also the incredibly useful method call sugar `table:func()`, which gets transformed to `table.func(table)`;
and function definitions like `function table:func() end` are sugar for `function table.func(self) end`. ain't that neat and simple, yet super useful?
% id = "01HRG2RJC23955SMQXHWEA202J"
- if you don't get the usefulness: this is needed because object oriented methods in Lua are implemented using regular functions; there is no magic `this` or `self` parameter.
the parameter is explicit, there is just sugar for passing it into functions and declaring functions with it.
+ it regularly happened to me that a type error I made only occured at _some point_ later during runtime; and then you have to track down a reproduction case and make a fix at the source. not fun.
- there's also the ugly case I had with a division by zero in the last rewrite of [Planet Overgamma][def:planet_overgamma/repo], which caused a NaN to propagate through physics and into rendering, causing a crash.
- there's [Teal](https://github.com/teal-language/tl) but last time I checked it didn't have support for inheritance, which is heavily used by [LÖVE](https://love2d.org/), which is my go-to Lua graphics framework.
- you can also compile [TypeScript to Lua](https://typescripttolua.github.io/), which is insanely silly, but has the advantage of using a language that's more familiar to a very wide group of people.
I wouldn't use it though because TypeScript and Lua are very different languages, and I'm afraid certain transforms would be unobvious - which would make interfacing with existing Lua code harder.
I think I prefer the bolt-a-type-system-onto-Lua approach of Teal in that regard.
- and it's really a bummer that Lua is not that strict!
% id = "01HRG2RJC2EGS11ERP93BY5BVK"
- global variables by default are a pretty bad design choice in my opinion. having any form of uncontrolled globals hurts local reasoning and makes it harder to tell whatever your code is going to do.
- but fortunately it is possible to freeze your global variables by overloading the indexing operators of `_G` - the table that represents the global scope.
```lua
setmetatable(_G, {
__index = function (t, k)
-- Only tell the programmer about undeclared variables. We still want access to
-- builtins like `require`.
if t[k] == nil then
-- The error message is purposefully generic because this will probably happen
-- the most when misspelling variables.
error("variable '"..k.."' was not declared in this scope")
end
return rawget(t, k)
end
__newindex = function (t, k, v)
-- Assigning to global variables usually happens due to typos with local variables,
-- so again - the error message is intentionally generic.
error("variable '"..k.."' was not declared in this scope")
end
})
```
% id = "01HRG2RJC229JBT8YQFE3P1V8C"
- there are also some bits of syntax that arguably haven't aged very well.
% id = "01HRG2RJC2N52T5X32YKPWP317"
- as much as people [complain about cosmetics][branch:01HRG2RJC1VJ5FDDM8X9ECB3HR], I think there's a particular design choice that has aged very poorly in the face of modern, functional programming - function literals.
these tend to be quite verbose in Lua which hurts readability in functional code:
```lua
local u = map(t, function (v) return v + 2 end)
```
compare that to JavaScript's arrow functions `=>`, which I think are a prime example of good syntax sugar that encourages more function-oriented programming: