Hacker News new | past | comments | ask | show | jobs | submit login

Lua's presence is honestly hugely because it is so easy to embed in other programs. If you needed some scripting capabilities, Lua was the way. Whether it's a webserver (nginx) or editor (vim) or a window manager (awesome), Lua was the easiest & best at hand option to integrate.

There is a huge opportunity, IMO, for more players here. Deno definitely has some good gestures toward a this. Wasm though seems like the likely general heir, and will have many different offerings for how to do that (Deno being one!).




> hugely because it is so easy to embed in other programs

Not only; Lua is also an example of excellent language design: a minimum of concepts for a maximum of expressability; it's minimal, complete and elegant.

> Deno definitely has some good gestures toward a this

Lua is much leaner, only ~100k machine code. WAMR looks like a better candidate than Deno, but concerning performance and simplicity it has still to catch up.


I think a "competitor" to Lua would be Guile [1], but I am not sure if it gets close to Lua in terms of lightweightness... it was designed to be used in the GNU project, with similar objects as Lua: to be light, easily embeddable. It's a Scheme (Lisp) so maybe not for everyone's taste... its "coolest" use i know of is for configuring Guix [2] (the GNU version of Nix).

[1] https://www.gnu.org/software/guile/

[2] https://guix.gnu.org


The competitors in practice were REXX and TCL. All three provide embeddable interpreters as libraries, and easy ways to extend what is available in scripts with custom domain-specific functions that one could call from them.


One of the reasons why Lua is minimal is that it makes it easier to embed. It's no coincidence :) https://www.lua.org/doc/cacm2011.pdf


Fascinating document, does anyone know if there's an html version?



I've just used `cloc`-ed Lua repo's entire code base, excluding makefile's and markdown's: 33619 lines in total of C, Lua, and C / C++ Header code; very lightweight code base.


There's a single C file you can compile really easily with `gcc -o lua luaall.c`. It compiles the whole thing in a couple of seconds!


You meant to say `onelua.c` file; indeed it's a fascinating language, I have to admit.


And that includes the test suite! Over 1/3 of that is inside the tests directory.


Lua 5.1 has 11'081 SLOC in 32 .c and 1'506 SLOC in 23 .h files (without tests and examples).


Forth and Lisp/Scheme and TCL are in the same league. However, they are more alien to C programmers, I assume.


TCL, Forth and Lisp are languages with minimum possible syntax, delegating all higher-level concepts to runtime structures and conventions (e.g. which list position means what), whereas Lua for its syntax and semantics inherited, pruned and elegantly integrated the best features of Modula-2 and other languages. It is true that Lua is more familiar to C developers than the mentioned languages, since both Lua and C look back to the ALGOL ancestry; but Lua - unlike C - clearly follows the Pascal heritage.


mruby[1] fits that category too. It can be used with H2O[2] server as well. There was a discussion here a couple of months ago regarding some use cases[3].

[1]https://mruby.org

[2]https://h2o.examp1e.net/configure/mruby.html

[3]https://news.ycombinator.com/item?id=34937297


So also Groovy. I have embedded Groovy in web applications to use as a rule engine. It is also very easy to install.

https://docs.groovy-lang.org/latest/html/documentation/guide...


It's not just that it's easy to embed but it's also tiny as well, I've run it on many embedded platforms. It wasn't until quickjs came along that there was anything even in the same ballpark.

When you factor in LuaJIT[1] and the incredible performance/zero-overhead-FFI[2] it really is a neat language.

[1] https://luajit.org/

[2] https://github.com/dyu/ffi-overhead


> webserver (nginx)

How timely! Right now I'm learning Lapis [1] by building a toy CRUD app that I plan on deploying to fly.io. Lua reminds me of Go insofar as it's a really "dumb" language. You can't get too cute and there are a few powerful abstractions that let you accomplish a lot. Coming from Python (which is an endless horizon of PEPs and build artifact standards and name mangling and kwargs and... well, you get the idea) it's kind of a breath of fresh air.

As for 1-based indexing: it makes more sense. We're all just used to 0-based because that's how C worked. If you stop and think about it early C was really just a veneer on top of assembly and since addressing an array in memory meant having a pointer to the first element that meant that it was arr[0]. We have no need for this decades later.

[1] https://leafo.net/lapis/


Oh you can totally get too cute with metatables. I've seen inheritance/straight-OO(Java/C++ style), component based design, and eventing/message based approaches all with the core primitives that Lua provides.

I think my favorite one was one that used yield[1] with the parameters being the number of frames to resume the execution of the coroutine. That was used by a number of our game designers to write AI scripting that felt close to literate programming(I.E. "move x, look for player, yield 30, if found player, go here, etc). You could build a whole AI state machine that fit into one page of code and was very easy to follow.

[1] https://www.lua.org/manual/5.1/manual.html#pdf-coroutine.yie...


Years ago I wrote a DNS-over-HTTPS proxy as an nginx / OpenResty module in Lua. The Lua code included a very stripped-down DNS message parser. The 1-based indexes and closed (instead of half-open) intervals were a massive pain in the arse. The code has a tricky mixture of odd and even offsets that would all have been even in C, and it would have been much easier to replace the magic numbers with named constants.

https://github.com/fanf2/doh101/blob/master/roles/doh101/fil...


> There is a huge opportunity, IMO, for more players here.

There are quite a few embeddable scripting languages [1]. I think these days it's less common to embed a language mostly because there are good high-level languages that applications can be predominantly written in.

[1] https://github.com/dbohdan/embedded-scripting-languages


Janet is another language that's just trivial to embed, but it's newer so it has less traction.


Really enjoying Janet, but it has some sad and seemingly unnecessary syntax differences to the rest of the lisps and lisp-likes.


I have ignored it because quasiquote is `;’ and that just pisses me off. I should not have such a strong reaction, but I do.


Yes, we shouldn't, but sometimes superficial features can be quite important.

I've ignored CL because it seems like the BASIC or bloated kitchen-sink of lisp/likes, but probably mainly because 'car'? 'cdr'? 'setq'? seriously?


But that's just how Lisp works. Emacs Lisp (not CL) uses those too. It's Scheme that decided to rename car and cdr (and by doing so obscure how things are implemented underneath).


But they're nonsensical.

I understand the history; some to-do with registers, but the names make no sense conceptually.


Scheme renamed them? When did that happen? R5RS? R6RS? R7RS? IEEE Scheme?


Okay, you're right. They are there as well. And actually the "first" and "rest" functions aren't even standard but are just there in some implementations as synonyms.


I know this part of the conversation is focusing on Scheme, but wanted to mention that first and rest are standard CL functions:

http://clhs.lisp.se/Body/f_firstc.htm#first

http://clhs.lisp.se/Body/f_rest.htm#rest


so it actually is like this: Scheme kept car and cdr.

Common Lisp added FIRST, REST, SECOND, THIRD, ..., TENTH and some others to the language standard in the early 80s.


lol, as I said, 'kitchen sink'. And then there's all that business with 'caar' 'caaddr', or whatever. I mean, just no.


Lisp is old. Thus software from several decades back can run.


Sure, but that's no reason to keep carrying past mistakes.

I don't understand why languages don't have a 'version' id as the very first thing, and then you can run appropriate interpreter/compiler given the version.

New versions of the language can drop historical mistakes.

You could kick-start the process in any existing language by making a next release that requires a version tag (and which maybe strips out a ton of legacy cruft). It would then default to a 'legacy' interpretation if the tag isn't present.


> I don't understand why languages don't have a 'version' id as the very first thing, and then you can run appropriate interpreter/compiler given the version.

Common Lisp may have that. My Symbolics Lisp Machine runs six different Common Lisp variants and additionally ZetaLisp side by side in one system.

There are often feature symbols which note language versions: CLtL1, CLtL2, ANSI90, ANSI-CL. It also has packages, where LISP will mean symbol table with CLtL1 symbols and COMMON-LISP will mean a symbol table with CLtL2 symbols.

> New versions of the language can drop historical mistakes.

Lisp may not work that way:

Lisp is not just a language, it is a running system. One can load many different libraries and programs into one running Lisp. Some older code will use CAR/CDR and some newer code won't. It still runs side by side in the same ONE environment and will call each other.

I can for example say:

#+ansi-cl (print "this is code for an ANSI CL compatible Common Lisp")

#-ansi-cl (error "this is Common Lisp does not support ANSI CL")

This will check if the Common Lisp has the features ANSI-CL.

Since cons cells and lists made of cons cells are so fundamental to Lisp, that it would be painful to support software where such a core data structure would be changed (for example by renaming the core operators).

Again, code from several decades with different language variants can be running in one Lisp.


In TXR Lisp, I made ^ quasiquote. That wasn't some run-to choice or anything; there is a history behind it. Before there as a TXR Lisp, only TXR, backquote was used for interpolated strings: `@foo @bar`, so the character was not available for quasiquoting.

In hindsight, ^ is nice because it stands out. I might have been influenced by ^ptr syntax from Pascal for dereferencing a pointer. Or exponentiation in some languages x^2. Hat denotes "power" and quasiquote is power!

(For a while I experimented with an idea of making regular quote ' somehow intelligent so it could do quasiquoting when unquotes are present. The semantic restrictions are too severe though and cannot be fixed: e.g. you can't do the equivalent ^(foo '(bar ,unquote-here)) with '(foo '(bar ,unquote-here)) where the unquote isn't going to the quote that we want.)

Because @ is significant, I had to turn ,@ into ,*. That's not too bad because * already denotes repetitions in regex: zero or more. And splicing brings in zero or more, so ...

The only issue is that if you want to interpolate a special variable, you have to write , *foo* and not ,*foo, because the latter actually means , foo*.

Common Lisp has the same issue with @, but @ isn't a popular symbol prefix, so it rarely comes up:

  [1]> (defvar @foo@ '(1 2 3))
  @FOO@
  [2]> `'(,@foo@)

  *** - SYSTEM::READ-EVAL-PRINT: variable FOO@ has no value
  [...]


  [4]> `'(, @foo@)
  '((1 2 3))


None of these options lift the `;` into something I have to parse as code, not documentation, so you’re all good!

:)


Ugh. I meant `;` is unquote-splicing, which is an operator within a quasiquote. Sorry for my brain fart.



For many purposes, Starlark[1] could be used as an alternative to Lua. It's also designed to be embedded, it's a very simple language, and has 3 implementations (in Java, in Go and in Rust).

[1] https://github.com/bazelbuild/starlark


I've honestly not found it easy to embed as-is, at least in C++. Getting functions into Lua is a challenge. Sol2 makes life soooo much easier for it.


LuaJIT has FFI capabilities, it's not a one-liner but it's also not bad:

https://gist.github.com/matiasinsaurralde/0a4ecf8f3e0d8768b3...


The problems I've had are passing data structures between...really gets rough.


Getting functions into Lua is my favorite use of macros in c++.


Easy embedding is one of the goals of FixScript [1] (my language). It consists of a single C file (and a header). It even includes a JIT while not being gigantic. It also provides a more direct C API which I think is more friendly for embedding.

It has strong support for backward and forward compatibility, a great feature for embedding usage to not make your scripts obsolete and generally working in any version of the host application.

[1] https://www.fixscript.org/blog/introduction


Its also the go-to language for lot of games when it comes to modding, or scripting things like custom missions or scenarios.


> Wasm though seems like the likely general heir, and will have many different offerings for how to do that (Deno being one!).

I was recently blown away by some ideas that StackBlitz [0] apply based on WebContainers. The idea of a "server in the browser", they allow you to run Node-based environment like that via Wasm.

[0] https://stackblitz.com/

[1] https://webcontainers.io/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: