Hacker News new | past | comments | ask | show | jobs | submit login
Lua: The Little Language That Could (blwt.io)
417 points by mooreds on May 28, 2023 | hide | past | favorite | 231 comments



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/


Anytime Lua comes up I'm reminded of John Earnest's Lil scripting language[1]. It's inspired by Lua and Q, built for Decker[2] which is a re-imagined version of HyperCard. Generally though, I love lua for its embeddability and am extremely happy anytime I see someone chatting about integrating it. Modding and scripting in games was a tremendous motivation for me to dig more into programming and these approachable languages are a core aspect of that.

[1] https://beyondloom.com/decker/lil.html

[2] https://beyondloom.com/decker/


For anyone looking for a HTTP server, or a Lua runtime with batteries included, have a look at the Mako Server[1]. It's really wonderful to work with.

I've automated my house based on it. It includes everything you may need , from JSON to TLS, email to websockets... all in a tiny 3MB binary, including the Lua interpreter!!

I run it on a Raspberry Pi and the process takes around 3MB of RAM. Just unbelievable comparing to anything like Node.JS or Java which would require a few 100's MB.

[1] https://makoserver.net


This is pretty amazing. I have a lot of Lua resources over at https://taoofmac.com/space/dev/lua, adding it there.


I think you accidentally added it to a new category, "Runtimes", instead of using the existing "Runtime" which had several entries?

EDIT: the Lua Lanes link is broken... the new location seems to be this: https://lualanes.github.io/lanes/


Thanks, will fix both.


Do you have a template of your configuration you could share somewhere?


You don't really need a template, I would say.

See the tutorials for what it looks like writing a server:

https://makoserver.net/tutorials/

Start with this:

https://makoserver.net/articles/HTML-Forms-and-LSP-for-Begin...

The docs you should read are mostly from the Barracuda Application server, which Mako is built on:

https://realtimelogic.com/ba/doc/?url=lua.html

You can even try this online:

https://tutorial.realtimelogic.com/Lua-Types.lsp

Click on "Okay, Got it" big button and you can follow along the tutorials, edit code on a demo server, which is how I actually developed my stuff... I actually used a mix of the online editor and editing the Lua files via SSH in emacs, via TRAMP. I highly recommmend either approach.


It is inevitable that people complain about Lua's 1-based "tables" which serve as both arrays and hash maps.

I usually encourage people to remember the practical origins of 0-based arrays when thinking in terms of pointers, where 0-based counting is useful in terms of talking about offsets from a point in memory, as contrasted to 1-based counting where you are primarily talking about quantities of collections.

Lua is a great language. My only wish is that it was easier to do multithreaded work in it. Unfortunately, this is not the case without some significant architectural forethought.


The reason why 0 makes sense for pointers isn't due to some arcane implementation detail of programming but because of how one conceptualizes ranges and distance and pointers just so happen to be a place where that comes up very directly in programming. Any time you attempt to develop an algorithm that needs to think about multiple ranges of a collection at the same time or needs to scale ranges through a collection, 1-based indexing causes you to have to throw in a bunch of +/-1 compensation factors to prevent you from double-counting or scaling past an edge. Life is just too short to have to constantly debug the issues that come from 1-based indexing when developing complex algorithms, even if it means beginners feel a bit confused by "counting from 0".


No, it actually does originate from 1960's era programming practices. Not to say there weren't also 1-based or N-based numbering, but you're historically misled here.

You could also say the same about counting collections. No regular person counts 0... 1... 2... 3... and ends counting N-1. It, too, is a workaround.


I don't think 0-based was ever as common a practice as it is today, especially in the 60s. In the beginning there were two camps fighting which system was to be preferred and there were many more 1-based or agnostic languages (like the whole Pascal family). "Numerical Recipes" a pretty popular book for some time even came in two editions, a 0-based and a 1-based version. I think the victory of C had a big influence on 0-based winning.

Personally I find it very sad that the agnostic approach of Pascal did not prevail. Let the programmer decide what they think fits the situation best and let the compiler deal with the technicalities. This, and I also miss range types... for like 40 years now...


> Personally I find it very sad that the agnostic approach of Pascal did not prevail. Let the programmer decide what they think fits the situation best and let the compiler deal with the technicalities. This, and I also miss range types... for like 40 years now...

I am with you there. However, Pascal, a toy Algol, was designed to be a language for a compiler course. It took its ideas like "where does an array start" from other Algol implementations and added a broken type system. Pascal gets the love now, as witnessed by the Lua designers referring to Pascal and not to Algol.


Surely everything was 0-based initially, when everything was written in assembly? Are there any 1-based ISAs?


I wouldn't call assembly 0-based. Sure, the zero flag is convenient for branching on zero when counting backwards but what makes assembly 0-based apart from that? I'd say having a pointer to the first element of an array is pure convention and you could as easily point it to the imaginary element before, similar to how the program counter always points to the next imaginary instruction on most architectures.


The offset addressing modes all start from zero. You could hack around it in some places by storing the pointer to an element before but that's clearly going against how it is intended to be used.

For example if you have an array that you cast between different sized types (e.g. uint64 to uint8) then you have to change the pointer value!


I don't think saurik is talking about history, so you're talking past each other in that sense.


I think so, too, but my point is that it makes sense for other historical reasons—not the one the poster mentions. Those decisions weren't made due to mathematics.


Agree. I came here to say that 0-based is more sensible, but as I thought through my arguments, I realised that I’m wrong. I do most of my coding in TS, Go and plpgsql. PL is 1 based, but the only time I find it frustrating is when translating between language boundaries. The rest of the time it’s just convention (eg, using <= instead of < in loops).

But I realised that I frequently want to get the last element in an array, and of course it’s easier with 1-based arrays.

So ultimately I don’t think it matters, except that most people are used to 0-based which can cause cognitive friction.


We aren't talking about counting. We are talking about indexing.

Most programs don't need numeric indexing. They can either treat indices as opaque keys or they can use a foreach loop to avoid touching indices at all (or friends like map, filter, zip, and so forth). Even in an old language like C the only place indices usually matter is in the first line of a for loop. But sometimes you really do need numeric indices, and when you do zero-based indices are almost always cleanest.

Just think of how gnarly a flat 2D array would be if you had to use one-based indices. Gross!


If the 1st element is [0], something's conceptually wrong.


You are right, but the thing that is wrong is that the word "first" is associated with the number 1!

Think about it. Natural language could easily have associated the initial ordinal with 0. Let's call it the Nilth (zeroth is really hard to say).

The nilth element is element 0, the first element is element 1, etc.

The winner of a race is in nilth place. Silver goes to the person who came first. Bronze so second.

You walk in to a building on the nilth floor, climb the stairs to the first floor (that might sound weird to Americans but it's how it works in other parts of the world).

This is probably all sounding crazy to you but it's way more elegant and self-consistent.

Which century is the year 2023 in again?


Some of us Americans know our elevator numbers are wrong — zero / nilth is the correct ground floor by far! — but that doesn’t mean when you pick up the first apple in a bowl of apples you should rename it to nilth. Counting order is different from Indexing order. One is the order of objects in a list and the other is the offset (2nd floor is two floors up from zero). They are just different things and that is why they sound right in different contexts!

(It’s 2023, and we both are right)


Correct, counting and indexing are different things. But the "first apple" is an index not a count.


[0] just means the address of the element is ptr+0. The first element is at the address from a memory addressing perspective it makes sense that it would be at [0]


Why care about the memory address for higher level languages like Lua?


The current element is at distance 0 from iter[].


When did the 21st century begin?


> 1-based indexing causes you to have to throw in a bunch of +/-1 compensation factors to prevent you from double-counting or scaling past an edge.

Way back then, I started with Algol 68, moved on to Turbo Pascal, and finally Fortran (I was young and needed the money). My vectors and arrays started at 1, as was the default, and I do not even know what you allude to. The first element has the index 1. Since there is no zeroth element - neither in language nor in thinking -, starting at 0 is what causes problems. You always have to use a corrective term in your head - the element five elements after the third one has index 7.

Yes, whatever.


My experience is that the +-1s come up less often in 1-based languages if you're writing high-level code, and when they do they're more intuitive to catch, even after years and years of coding 0-based.


The problem with 1-based arrays is that it makes the offset and location be different. For example, we want to place an item into a simple circular buffer with zero-based indexing:

arr[idx mod capacity] = 123

Now with 1-based indexing:

arr[(idx-1) mod capacity + 1] = 123

This is absolutely makes it a PITA if you're working with things like bitmaps, blending, or sound mixing. If you only need a language for high-level scripting, then it indeed is not such a big deal.


Works fine in Fortran, R, and Julia. People do pretty complex stuff in these languages and two of them are compiled.


It's not an insurmountable barrier, for sure. Just something that makes it very annoying in a lot of code. While not really making it easier anywhere.


Weird then that scientific computing languages prefer 1 based arrays. Maybe can show how it's very annoying in lots of Fortran or Julia code?


> Weird then that scientific computing languages prefer 1 based arrays.

Not really when the code is routinely transcribed from papers, and the ages-old convention throughout mathematics is one-based indexing, both bounds of the interval included. (With the notable exception of Euler’s beta and gamma functions.) Of course, in a paper intended for a human reader the occasional off-by-one error is much less dire than in code, so the author is freer to use a more error-prone convention.

I once had to translate from a one-based paper to zero-based Python code (with the added bonus of different discrete Fourier conventions *shudder*), and it’s remarkably annoying even if the resulting code is more obviously correct than the paper’s original formulas.


Python is as much a "scientific computing language" as anything, and it's zero-based.


Not really, the core language is general purpose. It's Python's scientific libraries and ecosystem which became popular.


You haven’t shown how it’s “very annoying” in R, Fortran, and Julia.


I've been working on a PR to document and make atomic pointer interface public in Julia. It's really not that hard to handle it under the hood so that array indexing and translates to LLVM and C zero indexing.


Lua tables support do 0 as an index. You can also use negative numbers or floating point numbers, which can be convenient for some applications.

One issue with using them that way is that some Lua idioms don't work as expected. For example, "for i,v in ipairs(t) do ... end" will start iteration with i=1 and stop when it reaches an index that isn't present. Also the length operator (#) won't work as expected.


What do you get when you get the 0-index? Does x[0]=x[1]?


Just reading the comments here, x seems to always be a hashmap. It is implied that there's no fundamental array type in the language. So no, x[0] and x[1] do not refer to the same variable, although they certainly could compare the same depending on their specific values.


The point here is that with the Lua code the CPU is doing some extra work because mod is 0 based because of mathematics and Lua is 1 based by design.

However mod itself is doing extra work compared to incrementing a counter by one.

This is pseudocode because I don't use Lua much and I don't have the time to check the syntax now, but it should give the general idea

  class CircBuf(size)
    buf = new Array(size)
    i = 1
    add(value)
      buf[i] = value
      if i == size
        i = 1
      else
        i = i + 1
The relative efficiency boils down to a comparison+increment vs mod on the particular CPU the code is running on. Mod should be a division, unless size is a power of two.


It is potentially odd (or even) but nothing compared to everything else when it comes to programming oddities or quirks.

What is the first item in this collection? Oh that would be the one in the box marked zero unless it should be in the one marked ... one!

You mention a circular buffer as an outlier but you simply have to decide where it (the buffer) started off life - 0 or 1 or something else. Make sure that when it is defined, everyone agrees on offsets and off you trot.

No it isn't particularly easy but then bitmaps and blending and sound mixing involve some very involved concepts that are way more complicated than sorting array bounds and worrying about where you are on the Magic Roundabout.


> You mention a circular buffer as an outlier but you simply have to decide where it (the buffer) started off life - 0 or 1 or something else.

No. A circular buffer is just a simple example where you need to go between offsets and locations.

The same theme happens in many similar cases, like directly manipulating bitmaps. E.g. with zero-based indexing into an RGB bitmap:

  bitmap[y*width*3 + x*3] = 1
vs. 1-based indexes:

  bitmap[(y-1)*width*3 + (x-1)*3 + 1] = 1


Lua’s arrays can start at index 0 if you like, there’s no real reason why the first line wouldn’t work.

Starting at 1 is a convention:

array = {“hello”, “world”}

print(array[1]) —- displays “hello”

array[0] = “why”

array[2] = “there”

for i=0,2 do print(array[i]) end —- “why” “hello” “there”


It's cute that you can do this, but it's not a practical solution, so I don't really know why people keep bring this up.

As shown by your example, arrays constructed the native way will start at 1. So you're going to need to make your own custom constructor which means additional overhead.

Also, all built-in functions that expects 1-based arrays will completely break:

    for _, value in ipairs({[0]="foo", [1]="bar"}) do print(value) end 
This will print "bar"

And even if you still insist on pushing through all of this friction by handrolling your own stdlib, everything is out of the window once you interact with any external library that you don't control.

There's a very good reason why no one is using this trick. Anything other than 1-based indexing is just undefined behavior.


There are different uses for `for’, and you’re supposed to use `for i=0, n do’ for the use case you’re describing.

People absolutely do this, and you have to for things like using LuaJIT’s FFI and doing work across C.


Yes that is annoying the first week, then you get used to it.

If the biggest complaint about Lua is 1-based tables, I’d say that’s a compliment to the language indeed.


Dijkstra had an opinion on this (and many other things). 0-based ftw.

https://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF


Dijkstra conveniently ignored the other situations where 1-based works out better than 0-based. For example, getting the least element of the array, or iterating backwards over the array.


This reminds me of that somewhat famous observation about the bright scientists working to increase clicks at Google/FB instead of finding cures for diseases and such.

So much brain power to defend something that existed a long time ago and was adopted as some kind of law of the Universe when there is clearly no good reason to be this way today other than "it's just how it is" and every new programmer has to pass for a quick phase where the 1-based it beaten out of his mind.


Bit short-sighted of him to only consider positive array indices IMO. Lua allows them to be negative.


Lua also allows floating point indices. In fact, any value can be an index into a table. That means any number, string, True/False, function reference, thread (coroutine) reference, table reference, "userdata" reference (usually a pointer to a C object), or nil can be an index.


Correction, any value except nil can be an index into a table. https://www.lua.org/manual/5.3/manual.html#2.1


I'm not going to complain about the indexing but I am going to complain about tables. The fact that you need at least one element to be in a table to trigger heuristics to serialize it properly makes Lua uniquely bad for use in polyglot distributed systems.

I've specifically run into this with OpenResty and JSON processing...


I would avoid trying to serialize tables in polyglot systems since it sounds like a specialized data structure whose counterparts don't really exist in other languages anyway.


It's a specialized data structure that the whole Lua ecosystem drives you to use as your primary collection type. They're blessed by the language, every library function will use will take and return them, and your deserializer will produce them, whether you like it or not.

Ultimately, you can use different data structures in Lua... but you'll have to write reams of boilerplate to do it. That's why I'm saying that (unfortunately) it's Lua itself that's a bad fit, not just tables.


I think it goes well with the loop syntax. In contrast to C/C++ where you do

    for(i=0; i<n; i++)
In Lua you do:

    for i=1..n
On the Lua case, both the beginning and end of the loop are inclusive.

If you have to iterate over n elements, the first syntax is optimized for 0 to n-1, while the second is optimized for the other case. See:

    for(i=1; i<=n; i++) // <n becomes <=

    for i=0..(n-1) -- ugh


The difference between 1-based and 0-based indices is irreverent enough, that’s the reason it feels uncomfortable for choosing to use something that is different. It is like little-endian vs big-endian, but you never see people choosing to use big-endian now


Just gonna add to the Lua love. I was also introduced to it by LOVE2D and revisted it with Redbean. In terms of ergonomics, it is still my favorite programming language; I find it very easy to read and reason about, and I credit that to its minimalism.

[1] https://love2d.org/

[2] https://redbean.dev/


I also got into Lua mostly through LÖVE 2D (there’s also LÖVR for 3D with a similar API, but different devs).

I’ve made a few small games using these frameworks in the past and as a hobby plan to continue making games with these frameworks, it’s fun!

Harvard also has a nice course on game dev with LÖVE 2D and Unity, CS50. Can be followed online for free [0].

———

[0]: https://www.edx.org/course/cs50s-introduction-to-game-develo...


Lua is a language in which I implemented a class system at a point at which I'd still be scratching my head over another language's syntax sugar. I love it.


For the past year I exclusively use Lua for my side projects. And I use it with certain self-imposed restrictions:

* no Luarocks

* no native libraries

More details: http://akkartik.name/freewheeling


That essay/software/discipline is awesome!


Problem is, which version of love to use? I do know someone had a multiversion love program but it didn't work for me.


Could you point me at it? Just for my own understanding.


I think its this but I am unsure.

https://github.com/Alloyed/flirt


I just looked at this. It feels awfully brittle. My goal at the moment is to try to support every version of LÖVE going forward. All my apps work with any version after 11.0. Hopefully that streak won't be too onerous to continue in the future.

Here's how my apps' version check works, based on suggestions from the LÖVE Discord (but mistakes are mine): https://git.sr.ht/~akkartik/template-live/tree/9cddb20b96aa1... I don't use t.version in conf.lua anymore in Freewheeling Apps (though my early apps are not perfect)


I've never used love. It's certainly difficult if you're going with portability, and then add to that backwards compatibility.

I've talked about this before, but I think the only way to do versioning is the way everyone does it nowadays. Have a directory, with a wrapper around the dvcs/(Commits/ Tags/) and then a subdirectory for a "git workingset", And then you Import("url",{tag="",commit="",req="x.y.z"}), it should be fast because you're just doing io.open($REPOHOME/$URL/TAG/$TAG/$COMMIT/x/y/z) which just gets dropped to require() as normal. of course, add LÖVE to the mix and I just don't know. love should have mandatorily a love-VERSION as the command.


Can you kindly provide a one line explanation as to why no Luarocks?


Luarocks works quite well if the lib is pure Lua.

For native libs it's a wild west and success depends a lot on your environment. Sometimes the lib is being built with -Werror and fails if you're using more recent compiler, sometimes include paths are not resolved correctly using pkgconfig or similar and so on.


https://xkcd.com/2347

That way lies npm and left-pad.

Tools encourage behaviors. I avoid tools that encourage behaviors I want to avoid.


how has your experience without using types in lua been so far with regards to finding out where issues reside and debugging unexpected values?


Great question! It's not ideal, but has been outweighed so far by the other benefits I mentioned. I do track the issues I run into due to lack of static types[1] in the Manual_tests files in each of my projects, e.g. https://github.com/akkartik/lines.love/blob/e568378ecb/Manua...

[1] I wouldn't say Lua/Python/Javascript has no types. They just lack types for variables rather than values. Forth, on the other hand, has no types period.


Great talk!


One of my favorite projects that uses Lua is

https://redbean.dev/

It’s a single file web application engine, and more, in an executable you can run on several OSes and I think even bare metal, but it’s been a while since I looked at it so it’s probably even more awesome today.


Yes! I'm currently learning Lua now just because of redbean.

I also recommend the excellent ZeroBrane IDE - https://studio.zerobrane.com/

As a Java/Python dev there is something about using such light weight and fast tools like redbean, lua and ZeroBrane that brings a smile to my face.


There's also the http://prosody.im/ XMPP server that's written in Lua, and it's very successful there. The other major XMPP server implementation is in Erlang and they are equally praised, so that should tell something about Lua's versatility.


The author fails to mention a super use case for lua which is celebrated by productivity nerds like myself - configuration.

I use neovim, hammerspoon which can be configured using lua richly. In a world of ever complicated software, I think lua is coming out as a gold standard for configuration files.


Wow, I just integrated Lua into a C++ project for the first time last night.

Took <15 minutes to integrate it with the build system (FASTBuild), watched a tutorial video[1], and then in another 15 minutes I had it hooked up to configure my application.

[1]: https://www.youtube.com/watch?v=4l5HdmPoynw


I'm surprised people dislike 1-based indexing. I use both types of indexing commonly and never had an issue with it.

The only issue I've come across in practice is lack of immutable tuples that could be used as table keys. I found myself either emulating this with nested tables or creating string identifiers for my multi-valued keys.


I dislike 1-based indexing, but I also don't really care that much. In my experience, people react with disgust to it, then get used to it, and still prefer 0-based but discover that 1-based isn't all that much more awkward in practice.

You get used to it pretty quickly when the whole language is built around it.

I solved the immutable table thing by encoding my tables to CBOR in the canonical representation. The same table structure always encodes to the same CBOR. I did have to write my own CBOR encoder, though, because the existing art wasn't good enough.


1 - based indexing is superior when thinking about actual collections, and not memory pointers. Most regular folks think of '1' index based. Eg. Most elevators (at least in NYC) start at floor 1, not 0. Once they start CS/programing classes they adjust to 0 thinking

0 - based indexing is better when dealing with pointers. But, none of us do today, (at least directly), and it seems that 0 based indexing is just an artifact of time.

When I used Lua for professional programming, I thought that 1 - based index is just better. I saw my self doing a lot less (-1) calculations.


We really don't want to use U.S.A. lifts as a gold standard, unless we want array indexing that unpredictably misses out array index 13 based upon the religious beliefs of the initial software developer. (-:


Australian buildings generally have "G" for "Ground" level (or something similar), then level 1 above that, so they're basically 0 indexed.


> Most elevators (at least in NYC) start at floor 1, not 0.

European buildings are generally 0-indexed: https://en.wikipedia.org/wiki/Storey#Numbering


The same applies to most former continental Europeans colonies in latin america and africa. 0 based indexing, sometimes with Floor 0 (argentina comes to mind) or simply ground (Terreo in Brazil)


Except people call it the "ground" floor which makes sense, not the "zero" floor which does not.


It’s not consistent but I would estimate that “0” buttons are as common as “G” buttons in elevators in Europe - along with -1, -2, etc for basement levels.


0 makes sense when you consider -1 to be the first basement level.


Which also gently introduces children to negative numbers : - )


In my building the 1st floor is on set of stairs/1 elevator ride up, the real first floor (i.e the ground floor) is labeled lobby (or a big green button in the elevator).

0-based indexing makes just a much sense at higher level abstractions, aka iterators because there you're more likely to think in distances and ranges than indices, iter.advance(0) => stay where you are, so if iter is at the start, iter+0 is the first element.


0-based also makes more sense when modular arithmetic calculations are involved, which is not that uncommon.


You can also use light userdata for that kind of thing.


Lua was born in 1993. That's 30 years, not bad for a language that some still consider new.

For those looking for a GUI library for Lua, check out TekUI. http://tekui.neoscientists.org/index.html

I tested it on various distros without hassle, both on x86 and ARM. It would probably be worth of porting to other languages too.


That’s got real Tk vibes right there!


I've been in game dev for decades, and I've never understood the desire for lua.

That's not totally true, it's easy to integrate and fast. But the language semantics and tooling leave a lot to be desired.

I'm hopeful we get an embedded version of another language. Something as easy to embed as lua, but without the lua. I'd much rather write JavaScript or Rust or anything else.


WebAssembly is pretty good at filling the same niche, but it takes a lot more work to interface between the guest and host than Lua.


I agree with you, I don't like Lua either. I especially don't understand why it's so good for embedding, and how people haven't implemented a subset of the project's original language as a scripting language, so that they don't have to write two very different syntaxes (and programming paradigms).


Implementing a subset of C or C++ as a scripting language is actually a lot of work, and is not fun to actually script in.


I have a hard time imagining that. PHP for example is fun to script in, and resembles a C/C++ more than Lua does. Of course fun is subjective, and I have no idea about the issues of embedding (why Lua works for that, and why other languages haven't got more traction).


Hey, I wrote this! Thanks for all the additional warm feelings about Lua on its 30th birthday year.

I have a "second part" in the works on this about implementing something a little more advanced in Lua, and some more exploration of Lua's warts.


I've started to view Lua as an extension to C rather than a language in its own right, which isn't really accurate, but reflects how well the two languages complement each other.


All the <3 for Lua.

Great language and great runtime(s)

Want to also raise awareness of TypeScriptToLua, a fantastic TypeScript transpiler for Lua: https://typescripttolua.github.io/


I hate how people are desperately trying to embed (C)Python in their programs just to hop on the hype-driven Python bandwagon instead of doing the sane choice and just relying on Lua, which was designed to begin with to do so. Lua is simple enough it can be thought efficiently in a few hours, and it's just so trivial to embed.

In comparison, CPython often requires to implement most of your code logic as a library and then load it into the interpreter, which is less than ideal.


I used to poke at the python API in blender and most of the wrapper work was done with magic.

Very rarely would you have to implement any custom C code aside from what you’d have to do with any other embedded scripting language to deal with the differences between the two languages and/or blender wonkyness.

I’ve also played around with lua embedding a bit — I just like python more so didn’t do anything with it. I do remember that I got it to work as an experiment but I would have had to start writing some serious C code (the kind you claim is unnecessary) to do anything useful with it and I’m pretty sure the maintainer of the game would have been opposed to being able to script things.

But, I’m one of those people who actually enjoys wrapping C libraries to use in python so…


A whole gamer generation has learned to make mods/games with this. (Roblox).

Not javascript.

Not python.

This.

That which educates the young, shapes the future, may the elders like it or not.


Pico-8 fantasy console uses Lua (a subset of; with Pico-8-specific console APIs). I keep going back to it in my personal projects time and again. I always think, "OK, I'm going to build the next thing in C" and wind up falling back to Pico-8 and Lua.

The in-the-works Picotron will grow from Pico-8's roots and present a full fantasy computer, with an OS, windowing system, etc. and expands Lua support to the full v5.4 language.


yep.

Roblox Lua/Luau tutorials on youtube - millions of views https://www.youtube.com/results?search_query=luau+roblox&sp=...

Luau

https://github.com/Roblox/luau

Roblox wrote a superset of Roblox Lua which is way faster https://luau-lang.org/


nice pointers, that sounds interesting. Have they kept it near-compatible with lua when it comes to how to embed it in a host app? Wondering if they are "competitive" in that aspect.


World of Warcraft addons as well.


That’s where I first encountered Lua.


Also that little monochrome handheld device with the crank, playdate, is all Lua.

Lucas Pope is developing for the platform now https://dukope.itch.io/mars-after-midnight


Garry's Mod was my first thought.


And Gmod


and crymods ce2


Another option for your weekend plan to learn Lua is to do it with Love2D [0].

Really easy, well documented and fun framework that uses Lua.

[0]: https://love2d.org/


Lua is great, I just wish it had typing


Nelua is mentioned in another comment.


Once neovim choose lua, I had a reason to pick up the language. I don't love it (mostly the 1-based indexing), but it's a lot better than vimscript.


Same here. And I picked up LuaLatex at the same time, by coincidence.


I'm currently using Lua as an extension language in my app [1]. Users can write their own custom UIs and DSP code. It's great how the Lua interpreter (and LuaJIT) allow you to provide your own memory allocator, so I can preallocate a region and do realtime-safe allocation within it.

[1] https://audulus.com


Hey just an fyi, but the screenshot on the homepage is 1.2mb in size. Might wanna optimize it a bit.


Looks cool. Is there any 5 min intro video introducing the app to the world?


Thanks! There are a couple preview videos on the App Store and lots of stuff on YT: https://www.youtube.com/audulus


Lua is also the language of choice for the nmap scripting engine.

While cute, easy to learn and all, I feel like it has kept back the development of nmap scripts. Only a few people are dedicated enough to take the time to learn it.

Then again, I think this has kept the quality of the available scripts high.


Well who knew nmap has a scripting engine? :)

Before reading your post, if i were in a situation where I needed to do complex stuff with nmap, i'd just integrate it in a script that would run it multiple times, parse the output etc.

I never imagined it had a scripting language inside itself.

Guessing the only people who know about that are admins in large networks and black or white hat crackers...



Oh yeah, if i were in a position to use nmap every day... Fortunately or unfortunately nmap --help has been good enough for me so far :)

> if i were in a situation where I needed to do complex stuff with nmap

Note: I've never been in such a situation. My network admin duties are like 0.5% of my time.


What are some of the nmap scripts that you use regularly?


Mentioning Openresty[1] (nginx + Lua + LuaJIT) is a must. I have been running production code with Openresty for the last 15 years. Smooth sail.

[1] https://openresty.org/en/


My first real exposure to Lua was as the configuration language of imapfilter. Absolutely loved both.

https://github.com/lefcha/imapfilter


The problem with lua - in my opinion is, the lackluster infrastructure for shared code.Lua rocks are version crippled and hard to integrate, bloat the code pretty fast (every instance has a full copy of all library code). Its also problematic that there are no default implementation for a API-Library layer. Which creates funny little re-implementations from v[1] to v.x and what have you not.


I'm not all that familiar with Lua so happy to be corrected here, but from what I know it's really small, really fast and really simple (to use), which makes me wonder why it's not used more often. Like I get it's not gonna be as fast as C, but apparently it's about as fast as C++. And it's not as simple as Python, but from what I've seen it's not far off. What's the catch?


There’s a lot of surface area to cover. In my opinion, the main thing that holds Lua back from wider adoption is the thing that makes it useful for its niche case. That is, the runtime and standard library are extremely small, making it easy and lightweight to embed in programs across a variety of platforms. However, that minimalist runtime and standard library also mean that it’s hard to deliver Lua libraries and frameworks without requiring native code extensions (e.g. any kind of networking and serious file system interactions) would require platform-specific extensions. Additionally, because Lua is so simple (for example there is no built-in concept of classes), the way to write code in Lua is quite open-ended. Many Lua users use their own or one of many prebuilt class systems. Using code from someone’s library might mean pulling in its custom class system and interacting with it as well. Custom class systems can be somewhat magical and involve metatable and suddenly things aren’t very simple anymore. Lua is also just a bit odd to use with one based indices, tables being kinda-sorta arrays if you use them in just the right way, needing to use pairs vs. ipairs in loops, variables being global by default if you forget to type “local” (unless you use Lua strict, and also all the libraries you want to use are compatible).


> What's the catch?

It doesn't have big sponsors.

Java had Sun and Google. Python, Kotlyn and Go have Google. C had AT&T. Basic and C# had Microsoft. Objective-C and Swift had Apple. FORTRAN and Cobol had IBM. Javascript and Rust had Mozilla, ...

Lua has a lot of small sponsors but no big one.

Very few languages succeed just on their merits, without big sponsors.


Lack of types hurts. Then there's the 1-base indexing that bothers many. The whole "tables" concept is kinda weird—and this makes more complex kinds of programming difficult/unergonomic. Luckily like JavaScript, there are a wealth of languages that compile to Lua to smooth over missing language features. One outlier feature is that Lua supports tail call optimization.


I find moonscript indispensable whenever I'm working with lua.

https://moonscript.org/


This is timely - we've been gearing up on Lua as we're building an open-source web app firewall [1] on top of Redis and whatever framework, and it's been a great experience.

Lua really unlocks some superpowers with Redis with respect to being able to chain multiple requests into a single call, handle some application logic, etc.

1 - https://wafris.org


Lua was on my very short list of languages to learn. Will need to revisit that.

Also worth noting, NetBSD has had lua hooks into their kernel for about 10 years I think.


You can learn Lua in a weekend, easy. The language is ridiculously small, but also ridiculously powerful. You can learn how to use C libraries in Lua in another weekend, easy. Using Lua from C is also easy, one weekend or less.


How about in 15 minutes lol ? https://tylerneylon.com/a/learn-lua/


Just curious: Can you share your list of languages to learn?


If you're looking for something like a "typed Lua" I suggest checking out Nelua (nelua.io).

It's almost 100% regular Lua syntax and compiles to C, so you get the nice syntax plus speed, similar to Crystal/Ruby. It's not perfect but is interesting nonetheless.


As a complete lua noob, can you run lua without C or C++? I've always thought of it as an embedded scripting language. I intend to learn it because of that but what can it do on its own?


Yes, there's a command line. Check LuaBinaries for that.

Also, if you want a fun way to play with it, see the LÖVE game engine. You can be making your own simple 2D games in next to no time. And I think that's a nice playground for trying out a language.

- https://love2d.org/ - https://love2d.org/wiki/Getting_Started


Lua can be run without using C or C++ just by running the interpreter. The shortcoming is that the standard library is intentionally very limited so there is very little you can do using Lua alone.

The main path is to implement with C or C++ bindings to the libraries you want to use and expose them as Lua module. This is the way Lua is supposed to be used.


Might be a little beyond me then since I've not experience with either C, or C++. Wouldn't know where to start.


There's Lua implementation [1] in pure Rust, by the way.

[1] https://github.com/triplehex/piccolo


Love Lua. They even added a compile time feature to address the fact that c++ exceptions and setjmp / longjmp are undefined behavior when used together.


When people talk about Lua's embeddability I hope they can just include a simple 20 line C snippet show how easy it is to actually embed Lua.


1. Download lua sources https://www.lua.org/download.html

2. Define your build for the sources (just compile them! No dependencies! Brilliant!) (don't compile lua.c and luac.c if you provide your own main)

3. Integrate with e.g. this main.cpp (if using c++ is your thing, just plain main.c or whatever is binary compatible):

#include <stdio.h>

extern "C" {

#include <lua.h>

#include <lauxlib.h>

#include <lualib.h>

}

// Native function to be called in Lua

static int add_numbers(lua_State* L)

    double a = luaL_checknumber(L, 1);
    double b = luaL_checknumber(L, 2);
    lua_pushnumber(L, a + b);
    return 1;
}

int main() {

    // Instantiate a Lua interpreter
    lua_State L = luaL_newstate();

    // Load Lua libraries
    luaL_openlibs(L);

    // Register native function in Lua
    lua_register(L, "add_numbers", add_numbers);

    // Call native function in Lua
    luaL_dostring(L, "result = add_numbers(10, 20)");
    // Read out the result of the function application
    lua_getglobal(L, "result");
    const char* result = lua_tostring(L, -1);
    printf("The result is: %s\n", result);

    // Define a function in Lua
    luaL_dostring(L, "function concatenate_strings(s1, s2) return s1 .. s2 end");

    // Call Lua function in native side
    luaL_dostring(L, "result = concatenate_strings('Hello, ', 'world!')");

    // Read out the result of the function application
    lua_getglobal(L, "result");
    const char* result2 = lua_tostring(L, -1);
    printf("The result is: %s\n", result2);

    // Close Lua interpreter
    lua_close(L);

    return 0;
}


Yeah this is a perfect example


Happy this helps!


Lua is a great little starter language.

I love that so many games use it.


Lua metatable > JavaScript prototype


The ConTeXt typesetting system tightly integrates Lua. One aspect of Lua I dislike is that it isn't object-oriented. What's impressive is that the language can be extended to be so in astonishingly little code:

* https://github.com/kikito/middleclass/blob/master/middleclas...

Using OOP, typesetting a hexagonal grid and a symbolic representation of a neural network on top is easier develop, IMO.

A vertex defines a point in 2D space:

* https://github.com/DaveJarvis/keenwrite-themes/blob/main/bos...

An edge connects two vertices:

* https://github.com/DaveJarvis/keenwrite-themes/blob/main/bos...

A graph connects edges:

* https://github.com/DaveJarvis/keenwrite-themes/blob/main/bos...

A priority queue serves for ordering edges by weight of adjoining vertices:

* https://github.com/DaveJarvis/keenwrite-themes/blob/main/bos...

With these concepts in hand, we can typeset a grid and a "neural network" on top:

* https://github.com/DaveJarvis/keenwrite-themes/blob/main/bos...

Here's an example of the output from the first chapter:

* https://i.ibb.co/19DCDZy/ch-1.png

And a later chapter, where the "network" has grown in complexity:

* https://i.ibb.co/ncf16vg/ch-2.png

This is for my near future hard sci-fi book on AGI. I'm looking for alpha readers to give me feedback. See profile for contact details.

P.S.

There's a subtle bug in the code that can result in one or two disconnected vertices. If you spot it, let me know!


It's actually not ConTeXt which integrates Lua, but LuaTeX; ConTeXt is based on the features of LuaTeX. Lua includes all features required for OOP (see e.g. https://www.lua.org/pil/16.html).


Yes, I was able to take advantage of this via lualatex to access METAPOST so as to do a parametric design for a CNC using OpenSCAD as a front-end:

https://tug.org/TUGboat/tb40-2/tb125adams-3d.pdf


Arrays start at 1. Just... no.


From an aesthetic perspective, I agree. In practice, it wasn't a problem, though. I rarely accessed arrays with a constant index anyway.


maybe I'm being dim but what possible aesthetic benefit does it offer? The only benefit that springs to my mind from a language design perspective is that it is most likely familiar to the user of the language from languages they have used before.

Both Smalltalk and APL use 1 based indices so I just find any claim of obvious aesthetic superiority on this issue a little hard to accept without some back up.

If speaking aesthetics the fact that the number of items in your array is equal to the position of the last index item in the array would seem aesthetically superior.


Lua's 1-based indexing is largely inspired by Fortran. Lua's first users were Brazilian engineers in the early 90s, who were mostly experienced in Fortran.

Arguably, one-based indexing is more closed aligned to mathematics tradition. That point about being easier to index the last element helps to iterate backwards over an array. With zero-based indexing, a backwards loop becomes for(i=N-1; i>=0; i--). Yuck.


exactly, I have a hard time understanding any argument for 0 based indexing other than tradition or a performance hack that I find unlikely was ever that great a benefit anyway.


It is admittedly a mindset that is born of C coding. Pointers and memory offsets and so forth. But lots of languages that abstract such "close to the metal" factors away, still use 0 based indexing.


I meant that aesthetically, I find 0-based indexing more appealing.


I like the arguement that 0-based indexing is better for representing offsets, but 1-based indexing is better for representing actual indices. https://hisham.hm/2021/01/18/again-on-0-based-vs-1-based-ind...


Why? 0 based indexing came from a time where you used memory addresses directly to index into arrays. Today, we don’t do that, so 1 based indexing seems extremely reasonable. Besides, with Lua you can still do 0 based indexing with a small personal extension function.


One of the first things requested by users of an internal media sequencing tool where time is measured by frames was 1-based indexing, they absolutely hated starting from 0


Yea, I love how 0 indexing is such a big deal. Like tabs vs spaces, it doesn’t matter even a tiny bit, it’s like a religion. It’s not that hard to index off of 1, and like I said, you can write like 5 lines of code to make 0 indexing the default!


Very insightful discussion.


Thank you. I'm here all week.. :-)


Nobody starts counting things at 0 in real life so… yes please!


I suppose it's whether you think of array indexing as an operation that offsets the read location relative to th start of the array. It's probably rather low level for the way people are used to working these days.


I think it's just common sense of pointing to the first location and saying "one" ("first element"), "two" ("second element") and so on.


Yeah that's a wart, but it's basically the only wart in an otherwise amazing language.


Ehh, local being a keyword (instead of local default, global only if required) was a significantly bigger gaff. The other would be no nulls in tables. 0/1 based indexing is familiarity only.


Requiring a keyword to declare a variable is a good thing. Otherwise, it'd be easily to accidentally declare new variables when you meant to assign to an existing one. Since global is the default, starting a program with something like this effectively keeps that from happening:

  local gindex = {}
  setmetatable(_G, {__index = function(t,k)
    local v = gindex[k]
    if v == nil then
      error("Bad read of global variable " .. tostring(k), 2)
    else
      return v
    end
  end, __newindex = function(t,k,v)
    error("Bad write of global variable " .. tostring(k), 2)
  end})
  for k,v in pairs(_G) do
    gindex[k] = v
    rawset(_G, k, nil)
  end


I once read through the entire Stroustrup C++ book and figured in one case, there is absolutely no way to make this work except for a secret, hidden variable in the class. Did a test compile and: There it was! OK, I get C++, done. Then I saw actual C++ programs. Whoa, how do you index into an array using a string? You don't of course, you overload the [ ] operator.

Reading about Lua when it was newer I could foresee the same thing happening on steroids. Yes, the language itself is simple but what you can do with it - emulate almost any construct in any language - promises confusion. When it comes to readable code, sometimes "less is more" in terms of langauge features.


> OK, I get C++, done.

> ...

> When it comes to readable code, sometimes "less is more" in terms of langauge features.

Please tell me that is meant as satire. There is no sane way to look at C++ and Lua and decide "less is more" would favor C++.


Let's put aside the god-awful 1-based tables, in 1993, which alone should have killed the language. My big problem with Lua is that it tries very hard to make possible all styles of language to all people, and as a result it is a language with a lot of boilerplate to do any one of them.

For example, consider Lua's OO. The most obvious inspiration for Lua here was Newtonscript, via Self. They're quite open about it. Newtonscript and Self are prototype-style OO languages done well, as opposed to Javascript, which is a prototype-style OO abomination. Lua advertises itself as being able to do prototype-style OO, all you have to do is make some tweaks to its metatables. But while this stuff is baked into Newtonscript and Self, it is an amazing amount of boilerplate to do it in Lua, to the point of parody. https://www.lua.org/pil/16.1.html


Why would you wish the death of a language only because its default index for arrays is 1?

You're not forced to index from 1 and onwards. You can use any range you want to: https://www.lua.org/pil/11.1.html


On the page you linked, I see four lines of boilerplate in a constructor:

  function Account:new (o)
      o = o or {}   -- create object if user does not provide one
      setmetatable(o, self)
      self.__index = self
      return o
  end
Doesn't seem so bad. Are you referring to something else?


You're way too triggered by a programming language, dude. Move past it.




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

Search: