Execution env is the achille's heel of scripting languages. Personally I don't use Neovim, but had a feeling its adoption would spur development in this area for Lua. Bryan Cantrill called Javascript "LISP in C's clothes". In some ways I feel like Lua is the opposite, and love it for those reasons (disclaimer: never had to use it at work).
Fennel has actually convinced me that Lua and lisp have more in common than one might think. I don't know what the above comment was referencing, but I've always found beauty in lisp languages for having a primary datastructure that all others can be abstracted to. Lisp, classically, has list, clojure has sequences, and Lua/fennel has tables.
Fennel is phenomenal, and is the reason I switched from chicken scheme to clojure/babashka despite hating the JVM. Neovim config, Wezterm Config, and various scripts to do things are all now written in fennel. I've also used it for game dev using Love2D.
It's a great great language, and fixes a LOT of the issues I have with Lua.
> Javascript is Lisp in C's clothes? On what basis?
On a lot of bases. Javascript has real lambdas, a sort of homoiconicity of code and data (hence JSON as a data format), also has the same dynamic take as lisps on "types belong to data". Rather than variables types belong to values. Brendan Eich's original idea was literally to "put scheme in the browser" and you can in fact pretty easily convert the Little Schemer to JS.
Saying two languages don't have much in common because they don't have the same syntax is a bit like saying we don't have much in common because we don't have the same hair color.
> a sort of homoiconicity of code and data (hence JSON as a data format)
I get that "sort of" was an attempt to hedge, but really, this isn't even close. Homoiconicity here would be if all javascript source files were valid JSON documents. A weaker version would be if it were common to use JSON to represent an arbitrary javascript program, but I've never heard of that, either. (For a good explanation of this weaker sense of homoiconicity, this stackoverflow page [1] is pretty good.)
To use Clojure as an example of a language that is homoiconic, you can take any Clojure source file, send it to an EDN parser (EDN being Clojure's equivalent of JSON), and not only will parsing succeed, but the result will be a complete representation of the program (you could execute it if you wanted to). In contrast, if you try to send JS to a JSON parser, you'll get an error as soon as it hits a function definition, or a for loop, or an operator, or whatever.
But MacCarthy's original LISP 1 and LISP 1.5 do not have real lambdas. The lambda feature parametrizes a piece of code as a function literal, but doesn't capture anything.
What they have is code parsed to a data structure, which is then susceptible to manipulation by the program before being executed. JS has some dumb textual eval, like the Bourne shell.
Javascript hotloading development setups are about the closest you can get to the REPL development loop outside of lisp. I'd imagine lua is similar if the embedding is set up for it.
Revise.jl in Julia (https://docs.julialang.org/en/v1/manual/workflow-tips/#Revis...) also gives a really neat REPL development experience. It allows tracking changes to any source code file, module, even standard libraries or the Julia compiler itself!
I hate to be that guy but a ton of languages have REPLs. The whole collection of smalltalks out there are basically an interactive environment. All of them forth languages do it too. Factor, Racket, LiveCode, there are so many. And for most of them, watching files and hotreloading is not how they do it.
To whoever down-voted me, please do explain how HMRs are comparable to Erlang's true hot code swapping or even Lisp's live redefinition.
HMR is limited to modules (almost or always UI components), and there is no native VM support (module boundaries, side effects, and global state complicates it further) for it, and there is no multi-version coexistence either, and it is absolutely not suitable for production.
To call "hot module replacement" hot loading is very generous, and quite an overstatement.
It is only very superficially similar to hot code swapping. It is a developer tool, not a foundation for live, fault-tolerant systems.
It is absurd to call hot module replacement (HMR) "hot reloading". It might sound fancier, but it is nowhere close to true hot code swapping.
Peak redefinition of terminology. Call it what it is: hot module replacement, at best.
Hot module reloading is common in JS land and does things like trying to preserve running state as opposed to just watching a dir and restarting everything.
Lisp killer features were GC, good data representation, first class functions. Lua has all that and more. But its being a "thin" library over the C runtime shows through the clothes.
Also, I do want to point out that despite very recognizable syntax, that's not the only thing that makes lisp lisp. Primary example of lisp-y syntax on a non-lisp would be Janet [0]
I would argue yes. There's a lot of things that I expect to work in a lisp/scheme that just don't work in Janet. Many of those things being related to cons/car/cdr stuff. That said, it's still a fantastic language.
I have an intuition that the comment you're responding to has a lot of truth to it, but I am going to have to educate myself to give you an answer.
One thing I do know is that JS and Lisp both treat functions as first-class citizens, allow some degree of meta-programming, and rely heavily on hierarchical (e.g., nested objects in JavaScript vs. s-expressions in Lisp).
Passing functions by reference enables both LISP and JS to compose higher-order functions and, as suggested in another commented, both Lisp and JavaScript's "dynamic stack frames" somehow live updates to running code without requiring a complete restart of the application. The only clear example of this I can find, however, is Bun's --hot mode, which performs a "soft reload," updating its internal module cache and re-evaluates the changed code while preserving global state.
I have some vague notion that this is a favorite feature of Lisp, but it's not clear to me that it's unique to these language families.
---
Edit: Lexical scoping, closures, some tail-call optimization...
---
Edit 2:
> Programming language “paradigms” are a moribund and tedious legacy of a bygone age.
(Dave Herman)[0]
---
Edit 3:
> The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."
> Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
> On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.
I know some projects like Koreader[1] use Lua as their primary application language. If you could convince one of them to switch, it would provide some assurances about the maturity and popularity of the idea.
That's a neat suggestion, thanks.
Lux will need some time to become mature, but building a large multi-platform project like koreader would definitely be a nice goal.
This sounds amazing. I use Lua a lot, and luarocks has been nearly useless for anything I need it to do because it's so heavily opinionated. For anything more than "install a library specifically for direct execution on the local system" or anything around that, it's a non starter. Have an embedded scripting environment that works with Lua packages and you want to package scripts with dependencies for use there? Give up.
I don't know if this is better for that use case, but even if not, luarocks is clunky and annoying to use at best.
the lua community is also incredibly dependent on C libraries, and nearly every luarocks package tries to build libs, which makes it basically useless on Windows
Useless if you need to run it in a scripting environment where you don't have control over runtime too. The thing lua was designed and is still prominently used for.
Interesting project. We'd love to work together for better Lua support in Pixi (through the conda-forge ecosystem). We already package lua and a few C extensions. C extensions are the bread and butter for Pixi, so I think it could be a good fit!
1. The commands, `lx run` and `lx lua` will set the `PATH`, `LUA_PATH` and `LUA_CPATH`.
And there's a `lx path` command for setting those environment variables.
2. It defaults to using pkg-config to detect Lua installations and will fall back to installing Lua via the `lua_src` and `luajit_src` crates if it can't find them.
We may eventually add support for other tools like vcpkg.
3. Not yet. It's on our roadmap to add support for that to our lux.toml/dependencies spec, but we probably won't allow rockspecs with that to be published to luarocks.org, because we don't want to be the reason people publish packages that can't be built by luarocks.
I have a strong opinion on this, as a Lua developer with some experience shipping Lua code.
Good third-party package management for Lua should function independently from the system-provided Lua installations.
It’s all too easy for newcomers to the Lua app-development world to tie themselves into some “auto-detected Lua” path that is, frankly, bonkers. It makes things hard to ship.
If you’re going to write a Lua app, and want to manage your Lua dependencies - use ‘luarocks —local’ and leave system-provided resources out of the picture.
Bundle Lua up front.
This is true of python, also: if you’re going to try to build an app that uses either Lua/python, one must take responsibility for building and bundling locally, and not using system-provided lua/python bindings.
Sure, use “brew —-prefix lua” to get things started, if that has to be a thing. Use pkg-config too, to build the “System/Package-Manager provided” lists.
One of the best practices for luarocks is to know and use the —-local flag with gusto and bravado, and include its artifacts in a larger app build/bundling target. This can be done in a cross-platform manner for all of the major platforms. Having a build target with a successful ‘luarocks —local’, for a locally built Lua and dependencies, does indeed rock. You can put everything in a .local/lua … and treat it just like any other linkable object resource.
If there is one solid rule for effective Lua development, it is: leave the system Lua’s alone, and build your own .local/lua tree, properly.
If you can’t do that, you’ll be missing out on the grand prize: being able to ship Lua with certainty that your Lua bundle will work, regardless of whats onboard the users’ setup … which is, after all, kind of a holy grail of the language, and therefore also a priority of its package management tools.
Another “Lua dev maxim” is, there are many Lua’s. There are reasons to use Lua 5.1 in a project - and reasons to use 5.4 and LuaJIT, too. Switching Lua’s is why I luaenv, but also, you can bootstrap a fresh Lua with CMake real fast, either as a project tool/binary, or directly to the VM abstractions, integrated into your app. A great Lua project has Lua 5.4 in the project, and both developer and end-user host binaries which include a Lua 5.4 environment, sandboxed, built from exactly the same sources. Bonus points for the Desktop/Mobile bridge getting crossed ..
So, depending on a system Lua, is repeating the same mistake prior lua package managers made, maybe. I have reached nirvana with luaenv+luarocks+cmake, personally.
I’m not suggesting lux not try to be self-aware on the basis of finding system standard tooling and libraries for its purposes, but that to be better than luarocks, lux will have to be able to handle —-local builds just as well ..
Luarocks is not just about being a ‘pip for Lua’, its also about having everything relatively tidy to support a fully local build and packaging target. With luarocks you can not use the system Lua, entirely - and that is a pretty significant use-case for luarocks, its utility in the packaging steps ..
The usefulness of a local Lua bundle depends on whether you're packaging a library or an application.
pkg-config can search by version, so you're not going to end up trying to build a Lua 5.4 package with Lua 5.2 with Lux. If you need a reproducible Lua installation, you can configure pkg-config - That works quite well in nixpkgs, for example.
Or, you can just disable pkg-config and let Lux install the correct Lua version for you.
We don't provide a way to dump a Lua bundle, but in principle that would be easy to add if users request it.
Coming from the other direction, luarocks binding to just one version of Lua, is an absolute pain in the ass.
I've got some libraries that support five or six different versions of Lua. Building packages for all of them basically means throwing luarocks out the window, because linking to multiple Lua runtimes ti=o luarocks does all kinds of truly heinous things.
You can end up with the library and headers linked from different runtimes, simply because it isn't following what the local system says is available.
A package manager written in Rust for a language designed to be embedded in C and which relies heavily on C libraries, and also configured in TOML when Lua itself was created to be used as a configuration language in C programs?
No, thanks.
Luarocks has its limits and probably should be rewritten, but using a language that fits the ecosystem and following the culture of the Lua ecosystem.
Rust and Cargo represent exactly the opposite of Lua.
Personally I'm really tired of all these language specific package managers. I just don't feel that is the right direction, stuff like nix seem like much better approach.
I have achieved this state of affairs, reproducible Lua package installs across machines, but I mostly use Lua either in a) “raw” form (linking to the VM internally) in which case I simply manage a .lua codebase as part of the project build, or b) as a system tool, wherein judicious use of “luarocks —local” and such tools as luaenv, have given me everything I need to stick in a Makefile/CMakeLists.txt file .. and to package into a bundle for distribution, a sprinkling of luastatic.
To be honest, none of this is any different for python, or any of the other scripted languages which might be found ‘onboard’; however, one must always differentiate between the system-provided /bin/script_language, and one which might be being used as a development tool/scripting engine in a larger project, or indeed .. local tooling .. workbench.
One of the reasons I like Lua so darn much, is that its really easy and fun to get all the libs packaged up, bytecode linked, bundle wrapped up, and have a single-click install for your intended users’ operating system, quite effectively.
Lua was designed for configuration, so I'd rather use Lua. Plus, it makes for a more elegant, unified design. TOML, and friends, are better paired with languages that were not designed for configuration.
It seems like every new language community and generation of developers needs to re-learn a set of the same lessons (why declarative instead of imperative config is the way to go, why functional is superior to imperative, why immutable is superior to mutable, why TDD (or just unit testing) is important, why hexagonal design is the best organization of logic, why static typing is important, why Nix is the only valid global package manager, etc. etc. etc.) and I suspect it's because these problems all share the same attributes:
1) the inferior solution seems easier at first (but won't be in the long run)
2) the people blithely picking the inferior solution have not yet encountered enough of the thorny nastiness that the initially-harder option completely prevents
the end result being that all the "greybeards" have settled on the "initially harder" solutions for things because they all learned the hard way that it just causes less headaches in the long run
I know what lua was designed for and have professional experience with it. It's not that great as a config language imo, I can easily understand why a project would use something else.
With the disclaimer that I know nothing about zig, I will say that Nix's flake files follow an interesting pattern where the output section is evaluated under the regular Nix interpreter and can do anything, whereas the input section is evaluated under a far stricter mini-interpreter that prohibits everything other than basic strings and attrsets (dictionaries).
A flake.nix is certainly not packaging metadata, but I do know those input section restrictions do cause occasional confusion with users who think "ah, Nix code" and only realise afterward that it's really json in Nix syntax."
All that to say, if a thing is not evaluate-able, it's probably best to represent it in a different enough form that no one could mistake it for a thing that can be evaluated.
Why not use a function to add dependencies, e.g. `add_dependency "dependency@version"` or `add_dependencies { ...table-here... }`. This is, more or less, how premake works [1]. For `lx add ...` you'd append the function call to the script.
I strongly prefer a declarative configuration instead of one that is fully-programmable. I want to be able to just look at a configuration and see what the settings are. Not have to account for the possibility that the config itself is also dependent on runtime variability.
What variability? Lua can be easily sandboxed to not take any inputs: Running it would express the same manifest. And most Lua configurations would still read declaratively unless they needed extra the complexity.
I just think it's a shame that the manifest file for Lua projects would be in a language other than Lua itself. I'm more sympathetic to other trade-offs, though, such as being able to edit the file mechanically.
With Lua, it becomes near impossible for a program like lux to edit the manifest.
For example, how would I `lx add <dependency@version>` if the `dependencies` table might be generated by a Lua function?
Lux defaults to TOML, but if you really want Lua, it also supports an `extra.rockspec` in the project root, which shares the same format as the luarocks RockSpec format.
The function would be "compile time" so that function would be evaluated when you call `lx add <dependency@version>`, I'd also assume that the previous configuration would be cached and a diff shown of dependencies.
But I would also accept that it would be a subset of Lua that could define tables, but not contain functions. The data only declarative sublanguage.
> most Lua configurations would still read declaratively unless they needed extra the complexity
This is precisely the problem. If you allow runtime configuribility then people will do it. I don't want my engineers to even have the option to meta-program the configuration file.
You just described in a concise way why I feel like “simple non DRY terraform configuration” is much better than “insane variable indirection to avoid like 20 lines of copy pasted code”
Configs are configs. It’s better for them to be obvious and verbose than it is for them to be hard to understand and pithy. They don’t have the same requirements as the software that actually goes out on the release train.
The problem, which I've run into with terraform code like this, is when you need to change something in those 20 lines, and forget about one of the places you pasted, then spend hours trying to figure out why one system is behaving differently than the others (especially if the person debugging is different than the person who made the change).
Indeed, environment drift is definitely a problem with the aforementioned approach. There is some middle ground that enables code and variable reuse, but isn't some byzantine state machine that requires reciting magical incantations to work. Getting that middle balance is kind of tough. I think judicious use of tfvars and modules and workspace tagging can get you a long way without opening the pandoras box that is workspace orchestration.
from what I understand is because if it was a Lua script, it would be impossible for their interactive CLI to manipulate the list of dependencies. Imagine if the list of dependencies would be generated by a function with side-effects inside that Lua configuration script, it would be really hard to make any tool to fiddle with that.
Thank you for treating neovim ecosystem first-class. During plugin development I missed ease of use of third party libraries I have with e.g. Rust and Typescript.
Has anyone used this? In my ~/.bash_profile, I have "eval "$(luarocks path --bin)"" which is very slow, and it needs to be called. How does this work with "lux"?
Additionally, I think there should be examples on the website of typical workflow(s).
Additionally, is "luvit" integrated in some way or another?
Additionally, does it work with LuaJIT? I assume it does, but worth a question.
> - Yes, it also works with LuaJIT, either via pkg-config or by installing LuaJIT headers using the `luajit_src` crate.
Are there any documentation on this?
luarocks works with luajit after I run that specific command, I expect something like that as it is very easy, so I wonder if running "lx path bin" would work.
This looks interesting. At one point I wanted to use a third party library using luarocks, but I gave up because it wasn't immediately working/straight-forward.
It may have been a skill issue though...
Yes, we have a `lux-lua` library (which we will need to bundle with `lux-cli`) that exposes a `lux.loader` module. It uses the lockfile to resolve dependencies.
I think moreso is the complete lack of any "batteries included" whatsoever. Not even something so basic as sockets are supported by Lua's stdlib. The ecosystem has had to fill so many gaps and a lot of that is dependent on Leafo and LuaRocks.
I'm hoping the great mlua Rust crate will help build the ecosystem since now it's mostly trivial to build easily-distributable native Lua packages in Rust.
Lua fills its niche of no-fluff embeddable scripting language perfectly, the idea is to bind to your own code. It makes sense to me that they wouldn't focus heavily on the standard library. I think that if there is a large enough community of people who want to use Lua as a general purpose scripting language, they should maintain an unofficial stdlib.
Oh certainly and I'm not arguing against Lua's decisions to have a small stdlib. Just commenting on why it has seen low adoption outside of specific niches like scripting game engines, etc.
I've been shopping around for a scripting language to replace Bash for commandline stuff, which also doesn't have a significant startup time, is Lua something I should look at?
The closest are Python, Ruby, Perl. IMHO Lua is best as plugin/scripting language operating on some object API. E.g. Nginx (OpenResty) and Roblox use it that way.
I have attempted to use Lua for shell scripting in the past and I agree with this assessment. I recently started learning Ruby specifically for shell scripting and find it to be the most ergonomic / productive.
Idk about lua, but uv single file python scripts (search the phrase) may be highly relevant for you. Or pure python, if you don't need to pull in any dependencies.
Python startup times suck though. On my system `python3 -c 'print("hello world")'` takes 60-70ms. In contrast equivalents with bash or lua both are in 1-2ms range. The difference is definitely noticeable.
Know your tools. If you're writing a script you run once in a while, 60-70ms is meaningless. If you're running it 10x a minute or more, sure reach for that fast startup language.
As comments here suggest, lua isn't much of a bash replacement. Happy to hear your alternatives and suggestions for bash.
I try to keep very few programming rules, but one which has emerged over time is "no python unless absolutely necessary" (precisely because of the thing you hinted at- the dependency problem, which I've run into one time too many, but also, the whole concept of there being only 1 way to do things which is kind of enforced just always rubbed me the wrong way)
But yes, in theory, that might be nice! There's also the Python-powered shell, Xonsh, which I can't use either.
Dependency problems are mainly resolved with uv - try it.
1 way to do things - not sure what you mean. It's a general purpose language, if you dont like something there's probably a different library that may do it the way you like (eg, don't like exceptions, use a library that mimics rust style Result values; don't like oop, don't do oop, functional programming is certainly possible in python), or easy enough to start creating your own private utility libraries that do things your way.
Xonsh was okay. Idk if I'd use it again. I'd probably just use python to invoke external cli programs using subprocess. I used xonsh for scripting before. I recall it having a slightly slow start up - I launch shell frequently so that's a no-go for me to use as my default shell.
---
And I looked at alternative scripting languages. Racket, D (has a single file script mode), ocaml, etc. Honestly python seemed good enough, even with its warts, especially with uv to manage deps without a whole formal project needing to be setup for a script. Stwp up from bash. Most of my use cases are fine with argparse + subprocesses + a unix shebang at the top + "chmod +x", with uv handy if I need a external library.
Btw: MicroPython is also available as a binary for all the usual operating systems. I’ve once made a few scripts and some .sh and .cmd files to launch them using the bundled micropython binary.
That's the main reason people have so mixed opinions about the language.
If you're stuck with the standard library (hello gsub!) and the application-specific library is an incoherent collection of functions, you're going to have a really bad time. That's the case too often because it's easier to plug the VM into an existing program than it is to create bindings that are not painful to use.
OTOH a well thought out environment makes you forget you're programming in Lua.
What I hate with passion is that the first thing you do is write your own pseudo-regex wrappers for missing string manipulation functions you'd consider standard in other languages.
I'd be happy for someone to correct me, but the Lua and LuaJIT repls don't seem very pleasant to use. Lua's standard library is also very barebones and... idiosyncratic, which makes it annoying to use as a Bash replacement.
I'm sure there are plenty of libraries out there to rectify those issues, though.
Perfect timing, I just added Lua integration to our product ( my bio has details ) for an AI agent to run code on.
Cannot wait to see Lua come back in full force. I had used it ages ago to build World of Warcraft plugins, then came back with Roblox, then back again for AI.
I also recently released a server for Server-Sent Events [0] that is programmable with Lua. Overall it's been a great experience. The Rust mlua-rs crate [1] is trivially easy to integrate.
Execution env is the achille's heel of scripting languages. Personally I don't use Neovim, but had a feeling its adoption would spur development in this area for Lua. Bryan Cantrill called Javascript "LISP in C's clothes". In some ways I feel like Lua is the opposite, and love it for those reasons (disclaimer: never had to use it at work).
Javascript is Lisp in C's clothes? On what basis? Also what does Lua have to do with Lisp? It has no Lisp syntax whatsoever.
Fennel has actually convinced me that Lua and lisp have more in common than one might think. I don't know what the above comment was referencing, but I've always found beauty in lisp languages for having a primary datastructure that all others can be abstracted to. Lisp, classically, has list, clojure has sequences, and Lua/fennel has tables.
https://fennel-lang.org/
And Tcl has strings.
Fennel is more popular than I expected! It's in the official repositories of Arch, Fedora and Debian.
as a Lisp-curious person, Fennel was a gateway drug for me
Fennel is phenomenal, and is the reason I switched from chicken scheme to clojure/babashka despite hating the JVM. Neovim config, Wezterm Config, and various scripts to do things are all now written in fennel. I've also used it for game dev using Love2D.
It's a great great language, and fixes a LOT of the issues I have with Lua.
> Javascript is Lisp in C's clothes? On what basis?
On a lot of bases. Javascript has real lambdas, a sort of homoiconicity of code and data (hence JSON as a data format), also has the same dynamic take as lisps on "types belong to data". Rather than variables types belong to values. Brendan Eich's original idea was literally to "put scheme in the browser" and you can in fact pretty easily convert the Little Schemer to JS.
Saying two languages don't have much in common because they don't have the same syntax is a bit like saying we don't have much in common because we don't have the same hair color.
> a sort of homoiconicity of code and data (hence JSON as a data format)
I get that "sort of" was an attempt to hedge, but really, this isn't even close. Homoiconicity here would be if all javascript source files were valid JSON documents. A weaker version would be if it were common to use JSON to represent an arbitrary javascript program, but I've never heard of that, either. (For a good explanation of this weaker sense of homoiconicity, this stackoverflow page [1] is pretty good.)
[1]: https://stackoverflow.com/questions/31733766/in-what-sense-a...
To use Clojure as an example of a language that is homoiconic, you can take any Clojure source file, send it to an EDN parser (EDN being Clojure's equivalent of JSON), and not only will parsing succeed, but the result will be a complete representation of the program (you could execute it if you wanted to). In contrast, if you try to send JS to a JSON parser, you'll get an error as soon as it hits a function definition, or a for loop, or an operator, or whatever.
But MacCarthy's original LISP 1 and LISP 1.5 do not have real lambdas. The lambda feature parametrizes a piece of code as a function literal, but doesn't capture anything.
What they have is code parsed to a data structure, which is then susceptible to manipulation by the program before being executed. JS has some dumb textual eval, like the Bourne shell.
They also have the concept of a symbol.
And only one value that is false.
JavaScript is not Lisp but it is more like Lisp than like C, even though syntactically it much resembles C.
ES6 JS has nice syntax for calculating with lists:
After the above 'car' has value 1 and 'cdr' has value [2,3].in that case Python is also a Lisp because it has:
https://www.norvig.com/python-lisp.html
https://hylang.org/
hmm Rust as well:
Javascript hotloading development setups are about the closest you can get to the REPL development loop outside of lisp. I'd imagine lua is similar if the embedding is set up for it.
Revise.jl in Julia (https://docs.julialang.org/en/v1/manual/workflow-tips/#Revis...) also gives a really neat REPL development experience. It allows tracking changes to any source code file, module, even standard libraries or the Julia compiler itself!
I hate to be that guy but a ton of languages have REPLs. The whole collection of smalltalks out there are basically an interactive environment. All of them forth languages do it too. Factor, Racket, LiveCode, there are so many. And for most of them, watching files and hotreloading is not how they do it.
Pretty sure they are just filesystem watchers. Correct me if I am wrong. Filesystem watching is NOT hot loading.
To whoever down-voted me, please do explain how HMRs are comparable to Erlang's true hot code swapping or even Lisp's live redefinition.
HMR is limited to modules (almost or always UI components), and there is no native VM support (module boundaries, side effects, and global state complicates it further) for it, and there is no multi-version coexistence either, and it is absolutely not suitable for production.
To call "hot module replacement" hot loading is very generous, and quite an overstatement.
It is only very superficially similar to hot code swapping. It is a developer tool, not a foundation for live, fault-tolerant systems.
It is absurd to call hot module replacement (HMR) "hot reloading". It might sound fancier, but it is nowhere close to true hot code swapping.
Peak redefinition of terminology. Call it what it is: hot module replacement, at best.
Hot module reloading is common in JS land and does things like trying to preserve running state as opposed to just watching a dir and restarting everything.
Have you used `bun --hot`?
Lua is C in Lisp's clothing.
Lisp killer features were GC, good data representation, first class functions. Lua has all that and more. But its being a "thin" library over the C runtime shows through the clothes.
Also, I do want to point out that despite very recognizable syntax, that's not the only thing that makes lisp lisp. Primary example of lisp-y syntax on a non-lisp would be Janet [0]
https://janet-lang.org/
It looks like fennel and janet are from the same dev.
Not having cons lists makes Janet non-lispy?
I would argue yes. There's a lot of things that I expect to work in a lisp/scheme that just don't work in Janet. Many of those things being related to cons/car/cdr stuff. That said, it's still a fantastic language.
Here's a thread a remember reading through at one point: https://news.ycombinator.com/item?id=26496339
I have an intuition that the comment you're responding to has a lot of truth to it, but I am going to have to educate myself to give you an answer.
One thing I do know is that JS and Lisp both treat functions as first-class citizens, allow some degree of meta-programming, and rely heavily on hierarchical (e.g., nested objects in JavaScript vs. s-expressions in Lisp).
Passing functions by reference enables both LISP and JS to compose higher-order functions and, as suggested in another commented, both Lisp and JavaScript's "dynamic stack frames" somehow live updates to running code without requiring a complete restart of the application. The only clear example of this I can find, however, is Bun's --hot mode, which performs a "soft reload," updating its internal module cache and re-evaluates the changed code while preserving global state.
I have some vague notion that this is a favorite feature of Lisp, but it's not clear to me that it's unique to these language families.
---
Edit: Lexical scoping, closures, some tail-call optimization...
---
Edit 2:
> Programming language “paradigms” are a moribund and tedious legacy of a bygone age. (Dave Herman)[0]
---
Edit 3:
> The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."
> Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
> On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.
-- Anton van Straaten 6/4/2003 [1]
0. https://cs.brown.edu/~sk/Publications/Papers/Published/sk-te...
1. https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/...
LISP is an assembly language with a interpreter stashed inside of it.
Dynamic dispatch and the Object System are bolted onto the side and are still unparalleled by any language that I'm aware of.
I know some projects like Koreader[1] use Lua as their primary application language. If you could convince one of them to switch, it would provide some assurances about the maturity and popularity of the idea.
[1]: https://github.com/koreader/koreader
That's a neat suggestion, thanks. Lux will need some time to become mature, but building a large multi-platform project like koreader would definitely be a nice goal.
This sounds amazing. I use Lua a lot, and luarocks has been nearly useless for anything I need it to do because it's so heavily opinionated. For anything more than "install a library specifically for direct execution on the local system" or anything around that, it's a non starter. Have an embedded scripting environment that works with Lua packages and you want to package scripts with dependencies for use there? Give up.
I don't know if this is better for that use case, but even if not, luarocks is clunky and annoying to use at best.
the lua community is also incredibly dependent on C libraries, and nearly every luarocks package tries to build libs, which makes it basically useless on Windows
Useless if you need to run it in a scripting environment where you don't have control over runtime too. The thing lua was designed and is still prominently used for.
Interesting project. We'd love to work together for better Lua support in Pixi (through the conda-forge ecosystem). We already package lua and a few C extensions. C extensions are the bread and butter for Pixi, so I think it could be a good fit!
- pixi.sh (docs) - lua package on the registry: https://prefix.dev/channels/conda-forge/packages/lua
That sounds like a great idea! I've opened [an issue](https://github.com/nvim-neorocks/lux/issues/550) in our repo. Feel free to ping us there :)
I don't see this listed here or the other associated sites, so I'll ask:
1. Does this integrate natively with `package.path` and `package.cpath`?
2. Does it detect non-standard, popular installations like through brew(1)?
3. Can you install by GitHub `:user/:repository`?
Also, neat project! Nice work.
1. The commands, `lx run` and `lx lua` will set the `PATH`, `LUA_PATH` and `LUA_CPATH`. And there's a `lx path` command for setting those environment variables.
2. It defaults to using pkg-config to detect Lua installations and will fall back to installing Lua via the `lua_src` and `luajit_src` crates if it can't find them. We may eventually add support for other tools like vcpkg.
3. Not yet. It's on our roadmap to add support for that to our lux.toml/dependencies spec, but we probably won't allow rockspecs with that to be published to luarocks.org, because we don't want to be the reason people publish packages that can't be built by luarocks.
Thanks :)
> pkg-config to detect Lua installations
I have a strong opinion on this, as a Lua developer with some experience shipping Lua code.
Good third-party package management for Lua should function independently from the system-provided Lua installations.
It’s all too easy for newcomers to the Lua app-development world to tie themselves into some “auto-detected Lua” path that is, frankly, bonkers. It makes things hard to ship.
If you’re going to write a Lua app, and want to manage your Lua dependencies - use ‘luarocks —local’ and leave system-provided resources out of the picture.
Bundle Lua up front.
This is true of python, also: if you’re going to try to build an app that uses either Lua/python, one must take responsibility for building and bundling locally, and not using system-provided lua/python bindings.
Sure, use “brew —-prefix lua” to get things started, if that has to be a thing. Use pkg-config too, to build the “System/Package-Manager provided” lists.
One of the best practices for luarocks is to know and use the —-local flag with gusto and bravado, and include its artifacts in a larger app build/bundling target. This can be done in a cross-platform manner for all of the major platforms. Having a build target with a successful ‘luarocks —local’, for a locally built Lua and dependencies, does indeed rock. You can put everything in a .local/lua … and treat it just like any other linkable object resource.
If there is one solid rule for effective Lua development, it is: leave the system Lua’s alone, and build your own .local/lua tree, properly.
If you can’t do that, you’ll be missing out on the grand prize: being able to ship Lua with certainty that your Lua bundle will work, regardless of whats onboard the users’ setup … which is, after all, kind of a holy grail of the language, and therefore also a priority of its package management tools.
Another “Lua dev maxim” is, there are many Lua’s. There are reasons to use Lua 5.1 in a project - and reasons to use 5.4 and LuaJIT, too. Switching Lua’s is why I luaenv, but also, you can bootstrap a fresh Lua with CMake real fast, either as a project tool/binary, or directly to the VM abstractions, integrated into your app. A great Lua project has Lua 5.4 in the project, and both developer and end-user host binaries which include a Lua 5.4 environment, sandboxed, built from exactly the same sources. Bonus points for the Desktop/Mobile bridge getting crossed ..
So, depending on a system Lua, is repeating the same mistake prior lua package managers made, maybe. I have reached nirvana with luaenv+luarocks+cmake, personally.
I’m not suggesting lux not try to be self-aware on the basis of finding system standard tooling and libraries for its purposes, but that to be better than luarocks, lux will have to be able to handle —-local builds just as well ..
Luarocks is not just about being a ‘pip for Lua’, its also about having everything relatively tidy to support a fully local build and packaging target. With luarocks you can not use the system Lua, entirely - and that is a pretty significant use-case for luarocks, its utility in the packaging steps ..
The usefulness of a local Lua bundle depends on whether you're packaging a library or an application. pkg-config can search by version, so you're not going to end up trying to build a Lua 5.4 package with Lua 5.2 with Lux. If you need a reproducible Lua installation, you can configure pkg-config - That works quite well in nixpkgs, for example. Or, you can just disable pkg-config and let Lux install the correct Lua version for you. We don't provide a way to dump a Lua bundle, but in principle that would be easy to add if users request it.
Coming from the other direction, luarocks binding to just one version of Lua, is an absolute pain in the ass.
I've got some libraries that support five or six different versions of Lua. Building packages for all of them basically means throwing luarocks out the window, because linking to multiple Lua runtimes ti=o luarocks does all kinds of truly heinous things.
You can end up with the library and headers linked from different runtimes, simply because it isn't following what the local system says is available.
A package manager written in Rust for a language designed to be embedded in C and which relies heavily on C libraries, and also configured in TOML when Lua itself was created to be used as a configuration language in C programs?
No, thanks.
Luarocks has its limits and probably should be rewritten, but using a language that fits the ecosystem and following the culture of the Lua ecosystem.
Rust and Cargo represent exactly the opposite of Lua.
Lua has evolved and is used for a lot more today than it was initially created for.
Personally I'm really tired of all these language specific package managers. I just don't feel that is the right direction, stuff like nix seem like much better approach.
One of the motivations for Lux is to improve the nixpkgs Lua and Neovim ecosystems.
A package manager for Lua that depends on Rust.
I see no problem though. Most package managers support binary-only package installations.
You might be surprised how well that works :)
And TOML.
I like this. I've been wanting to have a reproducible way to install Lua packages across machines for a while now.
I have achieved this state of affairs, reproducible Lua package installs across machines, but I mostly use Lua either in a) “raw” form (linking to the VM internally) in which case I simply manage a .lua codebase as part of the project build, or b) as a system tool, wherein judicious use of “luarocks —local” and such tools as luaenv, have given me everything I need to stick in a Makefile/CMakeLists.txt file .. and to package into a bundle for distribution, a sprinkling of luastatic.
To be honest, none of this is any different for python, or any of the other scripted languages which might be found ‘onboard’; however, one must always differentiate between the system-provided /bin/script_language, and one which might be being used as a development tool/scripting engine in a larger project, or indeed .. local tooling .. workbench.
One of the reasons I like Lua so darn much, is that its really easy and fun to get all the libs packaged up, bytecode linked, bundle wrapped up, and have a single-click install for your intended users’ operating system, quite effectively.
But yeah, you do sort of have to flex some limbs.
Why not use Lua for config instead of TOML? IIRC Lua was originally a data schema language, so it should be good for it.
Is there a good reason to introduce the halting problem to a package manifest?
Lua has a sandboxed runtime so couldn't you kill rogue scripts after a timeout?
Would you rather do that or use toml?
Lua was designed for configuration, so I'd rather use Lua. Plus, it makes for a more elegant, unified design. TOML, and friends, are better paired with languages that were not designed for configuration.
It seems like every new language community and generation of developers needs to re-learn a set of the same lessons (why declarative instead of imperative config is the way to go, why functional is superior to imperative, why immutable is superior to mutable, why TDD (or just unit testing) is important, why hexagonal design is the best organization of logic, why static typing is important, why Nix is the only valid global package manager, etc. etc. etc.) and I suspect it's because these problems all share the same attributes:
1) the inferior solution seems easier at first (but won't be in the long run)
2) the people blithely picking the inferior solution have not yet encountered enough of the thorny nastiness that the initially-harder option completely prevents
the end result being that all the "greybeards" have settled on the "initially harder" solutions for things because they all learned the hard way that it just causes less headaches in the long run
I know what lua was designed for and have professional experience with it. It's not that great as a config language imo, I can easily understand why a project would use something else.
With Lua, it becomes near impossible for a program like lux to edit the manifest.
For example, how would I `lx add <dependency@version>` if the `dependencies` table might be generated by a Lua function?
Indeed. I'm glad for us to have learned Python's 20 year lesson on the dangers of an executable manifest.
The Zig language seems to have learned the lesson the other way though: https://ziglang.org/learn/build-system/
Isn't build.zig.zon the equivalent of the manifest in that case though?
Sure, but a zon file is really just an anonymous zig struct.
With the disclaimer that I know nothing about zig, I will say that Nix's flake files follow an interesting pattern where the output section is evaluated under the regular Nix interpreter and can do anything, whereas the input section is evaluated under a far stricter mini-interpreter that prohibits everything other than basic strings and attrsets (dictionaries).
A flake.nix is certainly not packaging metadata, but I do know those input section restrictions do cause occasional confusion with users who think "ah, Nix code" and only realise afterward that it's really json in Nix syntax."
All that to say, if a thing is not evaluate-able, it's probably best to represent it in a different enough form that no one could mistake it for a thing that can be evaluated.
Why not use a function to add dependencies, e.g. `add_dependency "dependency@version"` or `add_dependencies { ...table-here... }`. This is, more or less, how premake works [1]. For `lx add ...` you'd append the function call to the script.
[1] https://premake.github.io/docs/Your-First-Script
That doesn't seem very ergonomic - especially for the use cases we have in mind.
Load the table. Modify. Serialize to file.
Not too hard. Emacs does it with the .emacs file, mixing generated and manual content.
> Load the table
If it comes from an impure function, you don't know if you'll get the same result each time you evaluate it.
> Modify. Serialize to file.
And potentially lose information.
I'm excited to check it out, but why does this use a .toml file when Lua was already designed for configuration files? Why isn't it just a Lua script?
I strongly prefer a declarative configuration instead of one that is fully-programmable. I want to be able to just look at a configuration and see what the settings are. Not have to account for the possibility that the config itself is also dependent on runtime variability.
What variability? Lua can be easily sandboxed to not take any inputs: Running it would express the same manifest. And most Lua configurations would still read declaratively unless they needed extra the complexity.
I just think it's a shame that the manifest file for Lua projects would be in a language other than Lua itself. I'm more sympathetic to other trade-offs, though, such as being able to edit the file mechanically.
With Lua, it becomes near impossible for a program like lux to edit the manifest.
For example, how would I `lx add <dependency@version>` if the `dependencies` table might be generated by a Lua function?
Lux defaults to TOML, but if you really want Lua, it also supports an `extra.rockspec` in the project root, which shares the same format as the luarocks RockSpec format.
The function would be "compile time" so that function would be evaluated when you call `lx add <dependency@version>`, I'd also assume that the previous configuration would be cached and a diff shown of dependencies.
But I would also accept that it would be a subset of Lua that could define tables, but not contain functions. The data only declarative sublanguage.
We want to be able to write updated dependency specs to the manifest.
> most Lua configurations would still read declaratively unless they needed extra the complexity
This is precisely the problem. If you allow runtime configuribility then people will do it. I don't want my engineers to even have the option to meta-program the configuration file.
https://en.m.wikipedia.org/wiki/Rule_of_least_power
You just described in a concise way why I feel like “simple non DRY terraform configuration” is much better than “insane variable indirection to avoid like 20 lines of copy pasted code”
Configs are configs. It’s better for them to be obvious and verbose than it is for them to be hard to understand and pithy. They don’t have the same requirements as the software that actually goes out on the release train.
The problem, which I've run into with terraform code like this, is when you need to change something in those 20 lines, and forget about one of the places you pasted, then spend hours trying to figure out why one system is behaving differently than the others (especially if the person debugging is different than the person who made the change).
Indeed, environment drift is definitely a problem with the aforementioned approach. There is some middle ground that enables code and variable reuse, but isn't some byzantine state machine that requires reciting magical incantations to work. Getting that middle balance is kind of tough. I think judicious use of tfvars and modules and workspace tagging can get you a long way without opening the pandoras box that is workspace orchestration.
It's cargo culting (pun intended).
from what I understand is because if it was a Lua script, it would be impossible for their interactive CLI to manipulate the list of dependencies. Imagine if the list of dependencies would be generated by a function with side-effects inside that Lua configuration script, it would be really hard to make any tool to fiddle with that.
It works for Elixir, really well I may add.
Thank you for treating neovim ecosystem first-class. During plugin development I missed ease of use of third party libraries I have with e.g. Rust and Typescript.
Has anyone used this? In my ~/.bash_profile, I have "eval "$(luarocks path --bin)"" which is very slow, and it needs to be called. How does this work with "lux"?
Additionally, I think there should be examples on the website of typical workflow(s).
Additionally, is "luvit" integrated in some way or another?
Additionally, does it work with LuaJIT? I assume it does, but worth a question.
- There's an equivalent `lx path bin` command, but there's also a `lx run` that lets you run installed packages.
- We're still quite early on in development, so documentation and things like error messages will need fleshing out.
- We don't have luvit integration yet
- Yes, it also works with LuaJIT, either via pkg-config or by installing LuaJIT headers using the `luajit_src` crate.
> - Yes, it also works with LuaJIT, either via pkg-config or by installing LuaJIT headers using the `luajit_src` crate.
Are there any documentation on this?
luarocks works with luajit after I run that specific command, I expect something like that as it is very easy, so I wonder if running "lx path bin" would work.
Only the `lx --help` and man pages.
I will take a look when I get into Lua again. :)
Nice! Lua really needed something like this to make packages easier.
This looks interesting. At one point I wanted to use a third party library using luarocks, but I gave up because it wasn't immediately working/straight-forward. It may have been a skill issue though...
In my experience, most people don't use luarocks anyway.
luarocks is easy to use, once you understand the importance of —-local all-the-things.
Anyway, luarocks is easy to use, and is perfectly easy to integrate into most modern build and distribution tools.
However, Lua is everywhere, so of course: ymmv. Disclaimer: I’ve been using Lua for decades now, its Just Great™ in my opinion.
luarocks has too many packages which require building C libs from scratch. It's basically unusable on Windows
And to make things worse, many of them do not compile successfully.
That said, luarocks is still easy to use, the problem is unmaintained packages.
as long as I'm in a vs developer prompt on mine I haven't had a problem. I did build my own Lua though.
They should, it is a breeze.
How do you deal with the diamond dependency problem? Do you allow two different versions of a package to be exist simultaneously?
Yes, we have a `lux-lua` library (which we will need to bundle with `lux-cli`) that exposes a `lux.loader` module. It uses the lockfile to resolve dependencies.
The website doesn't look that much luxuries, I would love to help with that.
You mean my Hakyll site? It's mot meant to be :)
Luxurious? What a pointless epithet for a package manager
finally, someone did it! luarocks was never an intuitive solution, and I feel this unintuitive ecosystem is one of the reasons for lua's low adoption.
> one of the reasons for lua's low adoption
I think moreso is the complete lack of any "batteries included" whatsoever. Not even something so basic as sockets are supported by Lua's stdlib. The ecosystem has had to fill so many gaps and a lot of that is dependent on Leafo and LuaRocks.
I'm hoping the great mlua Rust crate will help build the ecosystem since now it's mostly trivial to build easily-distributable native Lua packages in Rust.
Lua fills its niche of no-fluff embeddable scripting language perfectly, the idea is to bind to your own code. It makes sense to me that they wouldn't focus heavily on the standard library. I think that if there is a large enough community of people who want to use Lua as a general purpose scripting language, they should maintain an unofficial stdlib.
Oh certainly and I'm not arguing against Lua's decisions to have a small stdlib. Just commenting on why it has seen low adoption outside of specific niches like scripting game engines, etc.
I've been shopping around for a scripting language to replace Bash for commandline stuff, which also doesn't have a significant startup time, is Lua something I should look at?
The closest are Python, Ruby, Perl. IMHO Lua is best as plugin/scripting language operating on some object API. E.g. Nginx (OpenResty) and Roblox use it that way.
I have attempted to use Lua for shell scripting in the past and I agree with this assessment. I recently started learning Ruby specifically for shell scripting and find it to be the most ergonomic / productive.
Idk about lua, but uv single file python scripts (search the phrase) may be highly relevant for you. Or pure python, if you don't need to pull in any dependencies.
Python startup times suck though. On my system `python3 -c 'print("hello world")'` takes 60-70ms. In contrast equivalents with bash or lua both are in 1-2ms range. The difference is definitely noticeable.
Know your tools. If you're writing a script you run once in a while, 60-70ms is meaningless. If you're running it 10x a minute or more, sure reach for that fast startup language.
As comments here suggest, lua isn't much of a bash replacement. Happy to hear your alternatives and suggestions for bash.
OP did explicitly mention startup time as one of their criteria though
I try to keep very few programming rules, but one which has emerged over time is "no python unless absolutely necessary" (precisely because of the thing you hinted at- the dependency problem, which I've run into one time too many, but also, the whole concept of there being only 1 way to do things which is kind of enforced just always rubbed me the wrong way)
But yes, in theory, that might be nice! There's also the Python-powered shell, Xonsh, which I can't use either.
Dependency problems are mainly resolved with uv - try it.
1 way to do things - not sure what you mean. It's a general purpose language, if you dont like something there's probably a different library that may do it the way you like (eg, don't like exceptions, use a library that mimics rust style Result values; don't like oop, don't do oop, functional programming is certainly possible in python), or easy enough to start creating your own private utility libraries that do things your way.
Xonsh was okay. Idk if I'd use it again. I'd probably just use python to invoke external cli programs using subprocess. I used xonsh for scripting before. I recall it having a slightly slow start up - I launch shell frequently so that's a no-go for me to use as my default shell.
---
And I looked at alternative scripting languages. Racket, D (has a single file script mode), ocaml, etc. Honestly python seemed good enough, even with its warts, especially with uv to manage deps without a whole formal project needing to be setup for a script. Stwp up from bash. Most of my use cases are fine with argparse + subprocesses + a unix shebang at the top + "chmod +x", with uv handy if I need a external library.
Btw: MicroPython is also available as a binary for all the usual operating systems. I’ve once made a few scripts and some .sh and .cmd files to launch them using the bundled micropython binary.
Racket intrigued me, what did you find with it?
In that case, also check Zuo.
https://docs.racket-lang.org/zuo/index.html
Zuo is used to build Racket. It can be distributed as a single C file, which makes it extremely to integrate in new projects.
It's a really bad choice for a Bash replacement
You have to reinvent the wheel every other day since the standard library doesn't come with much included
That's the main reason people have so mixed opinions about the language.
If you're stuck with the standard library (hello gsub!) and the application-specific library is an incoherent collection of functions, you're going to have a really bad time. That's the case too often because it's easier to plug the VM into an existing program than it is to create bindings that are not painful to use.
OTOH a well thought out environment makes you forget you're programming in Lua.
I don't need to forget that I'm programming in Lua, I think it's a great little language
Just terrible standard library
Have there been any unofficial attempts to make a stdlib for it?
PenLight might count: https://lunarmodules.github.io/Penlight/
nah
it has no regex? eeek
What I hate with passion is that the first thing you do is write your own pseudo-regex wrappers for missing string manipulation functions you'd consider standard in other languages.
It seems that it has something that is close enough to regex to do the job most of the time
Yes you should.
You'll need a glob function, cd/getcwd/setcwd, pretty-printer. That's about it.
Lua stdlib replaces sed, awk, grep, cut, tr, tail, etc.
Startup time is the best. In my testing, a lua script that functions like `echo` runs faster than echo.
> In my testing, a lua script that functions like `echo` runs faster than echo.
this is kind of hilarious
lua must be severely optimized at this point
I'd be happy for someone to correct me, but the Lua and LuaJIT repls don't seem very pleasant to use. Lua's standard library is also very barebones and... idiosyncratic, which makes it annoying to use as a Bash replacement.
I'm sure there are plenty of libraries out there to rectify those issues, though.
I'm having a great time with Babashka in my ~/.local/bin
Standard to unify previous standards, huh?
https://xkcd.com/927/ ;)
(PS. I really cheer on Lua community having a decent package manager. Couldn't resist, though)
Perfect timing, I just added Lua integration to our product ( my bio has details ) for an AI agent to run code on.
Cannot wait to see Lua come back in full force. I had used it ages ago to build World of Warcraft plugins, then came back with Roblox, then back again for AI.
> Cannot wait to see Lua come back in full force.
I also recently released a server for Server-Sent Events [0] that is programmable with Lua. Overall it's been a great experience. The Rust mlua-rs crate [1] is trivially easy to integrate.
[0] https://tinysse.com/
[1] https://github.com/mlua-rs/mlua