Lua Notes

nelua quotes
Login

edubart quotes

why nelua?

but Nim opened my mind on what a programming language could do, and how you could code, for me this was an enlightenment, to the point I could not go back working with just C/C++, I felt in the position "damn I can't use Nim because of X,Y,Z bugs, also I can't use C/C++ anymore because it can't do X,Y,Z (that I could do in Nim), what should I do now?"
then I went looking up for other newer languages, all were not a good options for me at the time
I give a good try on Terra after Nim, which served as a second inspiration, and how I could use Lua to meta program

on language size

That's where some problem lies, when someones comes and say "hey this language could have safety as Rust", or X as Y. Then the language become more complex to conquer one more use case, and at some point the language becomes bloated because its trying to solve all use cases, and that's something I try to avoid in favor of minimalist and simplicity (in spirit of Lua).

on battling the compiler

Nelua has a design decision to not stand in the programmer way, that is, you are more free to do dangerous stuff (unlikely Rust or Zig)
but it does have compile-time and runtime checks where appropriate to catch bugs upfront, so the language is safer than bare C without standing in the programmer way
in my opinion compromising in that way makes the language more pleasant to code, so this is why the language do many implicit casting behind
this goes against Rust/Zig philosophy, where they make the user "battle" the compiler to get the code to compile

me: consider the 'no gc' path of D vs. Nelua. In D you add the attribute @nogc to a function and the compiler requires you to make sure that every dependent function also has that attribute. The experience then is

  1. make a decision to cut the gc out
  2. get a wall of errors that have to all be fixed before you can continue

In Nelua, you set pragmas.nogc and the default allocator is changed to the general allocator (i.e., malloc) instead of the GC. Everything still works, but garbage is no longer collected, and you can gradually deal with that. The experience then is

  1. make a decision to cut the gc out
  2. you now have a leaky build that can be gradually improved

There are obvious advantages to the D way of doing it. What I want to suggest is that there are also advantages to not doing things that way.

(cont.) on narrow casting runtime checks

I see.. it bothers me some times too, this is one example of a safe check to make the language safer but sometimes stays in the programmers way
this happens when passing a float to function that requires an integer, the options are:
  1. throw a error at compile-time anywhere you pass a float to a function that requires an integer
  2. crash at runtime when the float has a fractional part, example 2.3 => crash, 2 => 2 (works fine)
  3. truncate the float to an integer (no checks, it would work like C), example 2.3 => 2, NaN => 0?

Rust/Zig would choose 1. for example as it is the most safe option, but makes you battle the compiler all the time, specially in a language like Nelua where it tries to replicate Lua, and in Lua you can pass a float anywhere you expected an integer..
the 1. would be tedious to do explicit casting like (@integer)(x) all the time for a Lua user, specially in large code bases
the 2. is the option that I choose for Nelua, it does not stands in your way at compile time, and will never crash as long your code is correct
also Lua itself tends to work as 2, when you pass a fractional number somewhere Lua APIs expected an integer, it generates a lua error
the 3. would be the most unsafe, and hide errors from the user
in summary, we have to compromise somewhere, there is no perfect option, the middle term in my opinion and more Lua like was the option 2
but sometimes I consider changing to option 3...
and with -P nochecks, Nelua will work like option 3.

compiler speed

Nelua has different goals and design than V, it's unfair to compare compilation speed of Nelua vs V, because V compiler is static compiled, while Nelua compiler is dynamic, made in Lua and interpreted. But there is a good reason for that, having a dynamic compiler permits to have unusual meta programming possibilities in the languages, allowing the user to cooperate with the compiler via the preprocessor on the fly and extend the language and do unusual things usually not possible in other static compiled languages. Nevertheless Nelua compiler tries to be efficient as it can, I often profile and try to keep compilation optimized, because nobody likes huge compilation times, so usually you can expect better compilation times than Rust, C++, etc. But Nelua prioritize runtime efficiency and metaprogramming possibilities first. You can expect small programs to be compiled in a few milliseconds, but huge code bases I would say it could take some seconds.

on rewriting the Lua parts of the compiler to Nelua

would be faster, but this is out of my goals and vision, the compiler in my vision needs to be fully interpreted so it can be flexible for adding language extensions and meta programming
this is were Nelua is different from other new system programming languages like Nim/Zig/Odin, some of them have "an interpreter" with some capabilities at compile time, but not the whole compiler accessible and modifiable
in Nelua you can modify its syntax, ast nodes, type checking, code generation via the preprocessor, you can basically hijack the whole compiler if needed

GC + threading

You can use GC with multithreading as long you use any routines that uses GC in one thread, and the other threads you make sure no GC call is ever invoked. So this is doable however it's easy to make a mistake, therefore I do not recommend, unless you are adventurous.
GC with multithreading is a topic of research of its own, it's a complicated topic, I would never consider GC+multithreading to being with in favor of simplicity. Try to think async and use coroutines, Nelua supports them. Or try to think scaling with multiple app instances if you can, instead trying to use one instance with multiple threads.
Or use threads, but for very simple computation tasks, avoid any allocation/deallocation on extra threads. If you need to perform allocations, move ownership of non-GC allocations between extra threads, and you should be fine with memory.