It's nothing like C, and that's so much of its charm.
Semantically, Lua is almost identical to the core of JavaScript. Metatables are a genius alternative to prototype chains.
Lua's syntax is beautifully simple and unambiguous, but at the cost of being moderately inconvenient in 2025 unfortunately. It could benefit from an ESNext-style renewal.
I get why they made the C API that way, but in practice it's very easy to get wrong.
I'm not sure how fast vanilla Lua is today compared to similar languages. I think LuaJIT (and Luau?) are most often used when performance is needed.
FYI, for those who may not be aware, moonscript is the "coffeescript" for lua. It has been in production use for quite a while (the author of moonscript also created itch.io, using ... moonscript).
yuescript, from the dora-ssr game engine dev, is essentially moonscript-2.0
And of course, if you want to treat lua as the scheme-like it really is (deep down), then ... fennel.
Lots of choices. They all compile to straightforward lua, are very easy to incorporate (you can even compile at runtime, if you wish), and all employ full lua semantics, meaning zero runtime overhead
EDIT: and the curse of not reading fully ahead strikes again (doh!). Someone else has made the same points below ...
If Teal is the TypeScript of Lua, Fennel is the ClojureScript. Except Fennel is fully implemented in Lua itself and it transpiles (usually) to very simple, plain Lua code without much (if any) overhead and with no run-time library other than what is in Lua itself.
Sure, if you compare via semantics Lua and Javascript make sense to liken. But in terms of complexity, Lua is far more like C. There's no unfucking all the horrible decisions baked into javascript and I wouldn't touch it with a ninety-foot pole, but Lua still has some hope.
Like another commenter said, using . instead of : is maybe the most common mistake, too easy to make. And Lua offers no help preventing or checking it.
TypeScript is a great language. So is Lua. So is C.
When used carefully to avoid their warts. Learning how to do that for any language takes time and practice though.
Yea, and then there's javascript (or typescript if you prefer), the C++ of scripting languages. It's sometimes difficult to see any value through the warts. (Unless you're paid to, of course.)
Having a favorite language is weird (to me). They're tools, and some are more effective and usable than others, and some are better suited to some tasks than others.
But, equivalently, of course I'm going to criticize a hammer if it's literally covered in warts making it difficult to grasp without slipping. (or, if the gun I'm trying to use keeps firing bullets into my foot when I'm aiming down range.)
To be fair Lua also made some bad decisions, though maybe not as bad as javascript:
- tables being used for both objects and arrays can create a lot of confusion, especially when you have some integer keys, but not all, and especially when they are not consecutive or one of them is 0
- indexes start at 1
- assigning nil deinitializes variables/entries instead of assigning the value `nil` (this becomes especially bad if you mistakingly try to use nil as a value in an array/table)
- nil and false are falsy, but not 0, which instead is truthy
No disagreement here; I also fundamentally question the cardinal indexing and conflation between table and array. But it's at least internally consistent and certainly makes more sense than mandatorily indexing arrays with floating point.
Tables being dual purpose is fine. The real problem is that assigning nil deletes the table field. Unfortunately, fixing that now would cause Python3 levels of breakage.
Sure, but what are you comparing it against? C is certainly more straightforward to internalize than the semantics of C++, or Python.
Many of the corners of C understanding come with inherently abstruse backgrounds: say, accessing fenced memory with an aliased value in-scope, or in getting the particulars of expected memory-layout right for a given ABI. None of this actually impacts most daily development.
Hell, the impact of using zero-terminated strings is far greater of an issue than poor-specification of the language is. You have to deal with this problem; POSIX decided to bake it in to its core. Pointer mismanagement is a generally difficult problem, but C decided to actively cook-in hard-to-manage-strings.
I don't think it's a good language, and I hate it. I've made thousands of the same mistakes with it—typing . instead of :. There's a reason Lua has a smaller audience than assembly.
> There's a reason Lua has a smaller audience than assembly.
I don't think that's true. It's a very embedded language. Its use in video games and video game modding alone would outnumber the number of people directly writing assembly to achieve things.
It's also a very old embeddable language, which is probably most of why it's so often chosen for that purpose. Many similar languages have been written from scratch inspired by Lua over the last 10-15 years, some of them almost definitely better than Lua in many respects, but they get no traction because Lua has all the steam. Unfortunate, because the authors of Lua are fairly happy with semantic and syntactic decisions that many of us are very unhappy with.
It's got an awesome C API. It's fast, lightweight, and embeddable. It's more performant than Python. It's a staple in video game scripting.