I learned about Luau via my 13 years old who is looking into Roblox Studio. That's how I ended up visiting luau.org and I'm quite impressed by Roblox's engineering on this.
Arseny Kapoulkine is an amazing engineer. Highly recommend following his blog or social media. Other than working on luau and the rendering engine at Roblox, he's also responsible for meshoptimizer which if you're in graphics you've most definitely heard of, and volk, which now comes packaged with the Vulkan SDK.
Typed Lua is something I've always wanted, but writing a very comprehensive type-checker and LSP for another dynamic language is pretty difficult. All dynamic languages have similar problems to those TypeScript encountered, as most dynamic languages have a sort of structural typing in the form of dictionaries or objects.
I do wonder if we could reuse TypeScript in other dynamic languages.
Transform Luau to a subset of TypeScript, check with tsc, transform errors and results back to Luau. In the same way, one could reuse a TypeScript language server. This way of utilising TypeScript's engine could jump-start many other type checkers for other dynamic languages.
Luau already has Luau Language Server which works extremely well for vscode w/ nvim & zed support as well. It surfaces Luau's own diagnostics w/ autocomplete, strict type checking, etc., leading to a better DX (for me) than using Ruby or Python. I primarily use Luau as a shell scripting & general purpose programming language w/ my own runtime (ala node is to js) called seal. Many Roblox devs use a different (much more popular) runtime called Lune for Roblox CI/CD, unit & integration testing, etc.
Luau isn't "Lua but with types," but rather a language in the Lua family that has grown a powerful gradual type system with a _ton_ of type inference (this is in contrast to e.g. TypeScript which takes the approach more of "add annotations to get benefits"), as well as a growing number of additions to the language that we believe make it a more pleasant developer experience to use. We're very focused on preserving the general ability to embed the language readily and with low costs to binary size and so forth, but we're less ruthlessly committed to the language's simplicity (for both better and worse). Overall, the goal is an embeddable language related to Lua with a focus on developer tooling and a positive developer experience such that people actually want to (and enjoy) build(ing) software in the language, rather than e.g. build large C projects and then expose them to Lua.
Luau seems to be significantly more complex than Lua - I'm not sure it can still be called "small". Looking at the relative size of the implementations: Luau's is 120,000 lines of C++ [0], an order of magnitude larger than Lua 5.1's 14,000 lines of C.
But I think that complexity is unavoidable for a gradually- or statically-typed language. Any language with a reasonably-complete type system is inevitably going to be much more complex than a dynamically-typed scripting language.
[0] Counting *.cpp files in the "Analysis", "AST", "Compiler" and "VM" directories
I fully agree. Lua and Luau are impressive, sure, but they are not really "small" or "simple", in my view. I don't think the complexity is unavoidable however. There are many programming languages that are much simpler, but at the same time very expressive. I'm working on one of them currently named "Bau" [1], and I started working on a Lua-inspired VM [2] for a subset of this language. There are many languages like mine, most of them incomplete and not really popular, discussed in [3].
Lua (and to a somewhat lesser extent Luau) are small in terms of the learned surface of the value language, not necessarily in terms of lines of code. That being said, any runtime use of the language needn't depend on Analysis, which is the biggest compilation unit by far.
Probably also worth mentioning that Analysis currently contains two full type system implementations because we've spent the better part of the past three years building a new type system to address a lot of the fundamental limitations and architectural issues that we'd run into after years of working on the original one. The new one is not without issues still, but is definitely very usable, and gets better and better every week. At some point in the future, we will clip the old one altogether, and that'll shave off some 30,000 lines of code or something.
I have written a Lua type checker in the past and have delved enough into the Lua source code. For that reason I can say that Lua is particularly densely coded; Lua's 14K LoC is something more like 3--40K LoC when coded in the normal, less dense way. Lua is not necessarily small; it's just kinda concise.
I agree that static and especially gradual typing add complexity, but it's a very much smaller amount of complexity than we're talking about here, so in fact it is very common to encounter dynamically-typed scripting languages that are much more complex than some languages with excellent type systems.
I think you can implement a Hindley–Milner type checker in about a page of code, not the 2000 pages of code you're talking about.
I'm not sure what you mean by "complete". H–M is complete in the sense that it's decidable and doesn't leave any holes: programs that check statically are guaranteed not to have type errors at runtime. It handles higher-order functions and parametric polymorphism (generics) out of the box, it doesn't suffer from null pointers, and it can even handle mutability. And it's fully inferrable. There are various extensions to make it more expressive (GADTs, typeclasses, subtyping, linearity, tagged arguments) but even the basic HM system is already a lot more powerful than something like the type system of C or Java 1.7.
It's a shame that Lua did not evolve in a more backwards-compatible manner. In addition to Roblox, lots of others projects started adopting Lua 5.1 as a scripting language in the late 00s. Lua itself is now at 5.4, but it did not keep backwards compatibility. LuaJIT and related projects pretty much only support 5.1. It's similar to the situation Python had with 2.x/3.x, except that the majority of Lua users I am aware of are preferring to stay with the older 5.1.
I think it's even worse than that, luau and luaJIT have evolved in different directions than the official lua project, such that they are now all sublty incompatible with each others. They all branch from lua 5.1 but it feels like there isn't an offical standard anymore.
It's hard to get reliable numbers on this but I believe 5.1 and 5.2 are both more popular than 5.4 which has been out for five years now. And I don't think 5.3 ever surpassed either of them. I'm not sure about luajit it gets a lot of attention but I don't see it around all that much.
The huge difference is that the Lua community doesn't attack people publicly for maintaining backward compatibility, so it's generally pretty easy to write code that works across a wide range of Lua versions.
I know everyone hates bringing up naming conflicts, but I'm just going to say I think it's pretty lame to name a language so deeply inspired by another language, a name that is also insanely close to said language. Even the logo... I mean there's paying homage, then there's whatever this is.
I used to use Lua and later LuaJIT in Lumix Engine. I switched to Luau because of its type system. However, it's apparent it was not meant to be used outside Roblox, as it has many rough corners. The documentation is not great, and the community is basically nonexistent - I got zero results when searching for any issues I encountered. Also, it's huge compared to Lua or LuaJIT, causing my project to compile 7x slower. The API is not great (e.g., an async API that blocks, using STL in the API, leaking STL headers). I encounter bugs with analysis/LSP often. Overall, I consider moving away from it.
This has been posted here a while ago but I'm pretty excited about bolt: https://github.com/Beariish/bolt - I've been looking for a statically typed language which could integrate well around something with a strong type system and good meta programming facilities for generating bindings. Bolt has the benefit of being a pretty small library with the downside of being nascent.
I actually came across https://github.com/PlutoLang/Pluto and it seems to be all the things you would want from Luau but without the connection to Roblox
I can recommend that people check out Lune [1], it's a Luau runtime which includes a small but nice standard library. It's very easy to create something like a webserver in it. [2]
I liked what I read, although the size of the original Lua embedded interpreter has a particular aesthetic that any type inference feature takes away (in return, you get type safety, it's a fair trade-off).
But when I tried it without reading to much, I got this (bug or feature)?
Code:
--!strict
local x: number = 2;
print("Hello World!")
x="test"
print(x)
Output:
Hello World!
test
There wasn't any error message or warning when I pressed the run button, and that certainly violated my expectations, regardless whether the code and behavior may be considered "correct" (=documented) or not.
Second Life is switching over from Linden Scripting Language to Luau. It's working out well. The existing system compiled to Mono, and with Mono being deprecated (abandoned?) something new was needed. Amusingly, not only is Luau being supported, the LSL compiler is now targeting the Luau execution engine, and that works. Performance has improved slightly.
Second Life runs on hundreds of thousands of tiny programs, all event driven and running on the servers. Managing that is tricky, since server side resources are limited. Yet it works well, even if user programs are compute-bound. Actually, the biggest problem is that each idle program uses about 1us per frame, which adds up.
One thing that was immediately apparent upon switching VMs was that a lot of the existing overhead was in scheduling, context switching and the implementation of the actual library functions like `llDoWhatever()`.
We haven't even used Luau's JIT at all yet, but preemptive scheduling of what's typically trivial glue code is much cheaper and easier with a VM that supports it as a natural consequence of its design versus transforming everything into a state machine at the AST or bytecode level for a VM that doesn't.
> Actually, the biggest problem is that each idle program uses about 1us per frame, which adds up.
When they had the Luau beta regions up and running I gave it a whirl and it seemed performance was greatly improved over the old Mono system. Clicking "Save" was near instant to execute, great improvement to clicking save and waiting 10 seconds to know whether or not there's an issue in your script.
The only desire I have is if they could adopt FiveM-style helper functions which help wrap coroutines, namely being CreateThread(fn) and Wait(ms) (wrapper around yield inside that "thread") and Await/Promises (seems like someone already made an implementation for Luau? https://github.com/evaera/roblox-lua-promise)
FiveM adopting these makes it easy to make better performing scripts without having to mangle coroutines, which is vital given the Lua VM has to finish its current task before the frame is allowed to render.
35 comments
[ 2.6 ms ] story [ 57.2 ms ] threadI do wonder if we could reuse TypeScript in other dynamic languages.
Transform Luau to a subset of TypeScript, check with tsc, transform errors and results back to Luau. In the same way, one could reuse a TypeScript language server. This way of utilising TypeScript's engine could jump-start many other type checkers for other dynamic languages.
I had a pretty good experience with it while trying out Love2D.
[0] https://teal-language.org/
But I think that complexity is unavoidable for a gradually- or statically-typed language. Any language with a reasonably-complete type system is inevitably going to be much more complex than a dynamically-typed scripting language.
[0] Counting *.cpp files in the "Analysis", "AST", "Compiler" and "VM" directories
[1] https://github.com/thomasmueller/bau-lang [2] https://github.com/thomasmueller/bau-lang/blob/main/src/test... [3] https://www.reddit.com/r/ProgrammingLanguages/
- Analysis: 62821 lines of C++ code, 9254 lines of C headers
- Ast: 8444 lines of C++, 2582 lines of C headers
- CodeGen: 21678 lines of C++, 4456 lines of C headers
- Compiler: 7890 lines of C++, 542 lines of C headers
- VM: 16318 lines of code, 1384 lines of C headers
Compare to Lua 5.1, which tokei says has 11104 lines of C and 1951 lines of C headers in the src/ directory.
Probably also worth mentioning that Analysis currently contains two full type system implementations because we've spent the better part of the past three years building a new type system to address a lot of the fundamental limitations and architectural issues that we'd run into after years of working on the original one. The new one is not without issues still, but is definitely very usable, and gets better and better every week. At some point in the future, we will clip the old one altogether, and that'll shave off some 30,000 lines of code or something.
I think you can implement a Hindley–Milner type checker in about a page of code, not the 2000 pages of code you're talking about.
I'm not sure what you mean by "complete". H–M is complete in the sense that it's decidable and doesn't leave any holes: programs that check statically are guaranteed not to have type errors at runtime. It handles higher-order functions and parametric polymorphism (generics) out of the box, it doesn't suffer from null pointers, and it can even handle mutability. And it's fully inferrable. There are various extensions to make it more expressive (GADTs, typeclasses, subtyping, linearity, tagged arguments) but even the basic HM system is already a lot more powerful than something like the type system of C or Java 1.7.
See https://luajit.org/extensions.html.
Unfortunately, there is not a Luau distribution of windows like Luarocks.
Eventually we may see something in this place.
The well known libraries, IUP, CD, IM have not been ported to Luau.
But code is Free Open Source, who knows.
To me, this is the more interesting bit of luau
The performance page[1] contains a pretty good explanation of the work they have done. Pretty impressive engineering if you ask me.
[1] https://luau.org/performance
https://github.com/luau-lang/lute
[1]: https://lune-org.github.io/docs/
[2]: https://lune-org.github.io/docs/the-book/5-networking/#runni...
But when I tried it without reading to much, I got this (bug or feature)?
Code:
Output: There wasn't any error message or warning when I pressed the run button, and that certainly violated my expectations, regardless whether the code and behavior may be considered "correct" (=documented) or not.Second Life runs on hundreds of thousands of tiny programs, all event driven and running on the servers. Managing that is tricky, since server side resources are limited. Yet it works well, even if user programs are compute-bound. Actually, the biggest problem is that each idle program uses about 1us per frame, which adds up.
We haven't even used Luau's JIT at all yet, but preemptive scheduling of what's typically trivial glue code is much cheaper and easier with a VM that supports it as a natural consequence of its design versus transforming everything into a state machine at the AST or bytecode level for a VM that doesn't.
> Actually, the biggest problem is that each idle program uses about 1us per frame, which adds up.
More scheduler overhead to resolve :)
The only desire I have is if they could adopt FiveM-style helper functions which help wrap coroutines, namely being CreateThread(fn) and Wait(ms) (wrapper around yield inside that "thread") and Await/Promises (seems like someone already made an implementation for Luau? https://github.com/evaera/roblox-lua-promise)
FiveM adopting these makes it easy to make better performing scripts without having to mangle coroutines, which is vital given the Lua VM has to finish its current task before the frame is allowed to render.
https://github.com/citizenfx/fivem/blob/master/data/shared/c...