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

This is an unpopular opinion, but I wish Lua had won against python. It integrates seamlessly with C and for an interpreted language it’s super efficient. Rather than including every library under the sun in the standard lib, Lua is compact and the entire compiled binary is measured in kilobytes. You can read the Lua source code in C and comprehend a lot of it in a weekend.

Lua Rocks package manager includes basically every library you could want. Lua doesn’t even include proper object oriented programming, but is so powerful you can add support for it quite easily, and is a good learning experience for aspiring Lua programmers.

Lua is a great language, I wish it was mentioned and compared against more popular languages like Python and JavaScript.

> I wish Lua had won against python

Thank goodness it didn't. The number one benefit you should get from a scripting language as opposed to a compiled one is higher productivity – otherwise, you could just write it in C and have higher performance and lower resource usage. What good is a scripting language where I have to either implement every utility function myself, or look for and include an external library? Lua does not even have a round() function! [1] You can't just write print(some_table) [2], which makes using the REPL needlessly burdensome.

Sure, as an embedded scripting language in very small, resource-constrained devices this is a good strategy, but that's a niche application.

Even worse is the "arrays are hashtables in disguise" concept, because even though it might appear to be elegant, it doesn't work well in practice. In the end, you have to treat arrays and hashtables differently (see, e.g., pairs() and ipairs()), because – big surprise – arrays and hashtables are very different conceptually, so you don't gain much from unifying them.

Also great is that if you set element #5 in a 10-element array to nil (maybe you write foo[5] = arg1, and arg1 comes from a caller), your array suddenly only has 4 elements. That's because the first nil in a hashtable demarks the end of an array. Similarly, you might get an array argument from a caller, check its length (4 elements), append a single value, and suddenly your array has 10 elements, instead of 5! That's terrible design.

[1] But it does have functions to convert degrees to radians and back. How is that more important than round()?!

[2] Lua will just print the address of the hashtable, which is completely useless if you want to see its contents.

> The number one benefit you should get from a scripting language as opposed to a compiled one is higher productivity

As I understand it, neither lua nor python are compiled languages, they're both dynamic, scripting languages. Which one do you think is a compiled language?

> The number one benefit you should get from a scripting language (Lua) as opposed to a compiled one (C) is higher productivity

OP is saying that since Lua is cumbersome to write you might as well write in C and get increased performance.

I tryied to read it- but in the second sentence the well rounded argument i expected, became a syntactic sugar and base library bitching about round not being part of the base.

That this bubbled up, sums up the true power of javascript. No good arguments, but lots and lots of people who know and use it. Lua should just attach itself via script converters to that ecosystem and convince via time and better syntax the unwashed masses.

The lua interpretors are just fine ; it's not because it's designed to be easily embeddable that it doesn't work just fine on its own.

> The lua interpretors are just fine

I don't think I complained about the Lua interpreter itself?

> it's not because it's designed to be easily embeddable that it doesn't work just fine on its own.

I don't understand that sentence.

Yeah sorry I guess your comment wasn't about that. But I don't understand the original comment's point.

What is the problem you describe about Lua? That you can't just say "import module" and have many modules available? Sure you can if you have a distribution that provides them, which I think is the kind of thing that the previous comment was referring to. What's the big difference with Python?

I guess the lack of round function is surprising too, but the explanations I see online about it being a minimalism and ease of implementation make sense. I think it's a fair choice and I don't think a core library's choice should be an argument to judge the whole language.

> But I don't understand the original comment's point.

1) A minimalistic scripting language is appropriate for a few niches only, therefore Lua would be a bad replacement for Python in most cases.

2) Missing basic functionality (e.g., printing the contents of the language's fundamental datastructure) decreases productivity, the one thing which scripting languages are supposed to excel at.

3) Pressing arrays into hashtables-as-arrays-but-not-really is a very bad, unnecessary design decision with few upsides and many downsides.

> I guess the lack of round function is surprising too, but the explanations I see online about it being a minimalism and ease of implementation make sense.

I have read those implementations and they are all unsatisfying:

1) The "minimalism" argument: How is that consistent with the existence of math.deg() and math.rad()? Both are easier to manually implement and less rarely used than round().

2) The "ease of implementation" argument: The C standard library actually provides a round() function, so binding it would be negligible in terms of both resources and implementation effort.

> I don't think a core library's choice should be an argument to judge the whole language

The lack of round() is symptomatic of Lua's standard library, it's not a singular example. Another one is that there's no function to copy a hashtable (neither for shallow nor for deep copies). How can a language offer such a bad support for its most important datastructure?

I think Python's standard library is super underestimated.

I needed to work on zip files the other day and the Python standard lib included a great implementation with high quality documentation. Languages with minimal stdlibs tend to fall flat on the documentation front, and it adds a lot of friction imo.

Though lua being embeddable is very nice, just coming from trying to embed Python and having a bit of frustration at managing it.

I think we could use a reasonable middleground between the awesome standard libraries of Python and Java and the ocean of tiny packages of Rust and Javascript.

It'd be nice if a package-managed language could import the huge awesome standard library, automatically drop whatever goes unused, and that mechanism worked well enough that everyone but the most restricted embedded programmers were fine with it.

Totally. I think that if you can build your dependency graph properly you don't need to package the standard library.

The standard library exists partly because packaging was hard, so it's easier to ship a bunch of stuff. But nowadays it could simply be "core libs" that get pulled in on use.

Packaging is only part of the whole equation.

Quality, available documentation, guaratee of supporting every OS targeted by the language toolchain, and future updates in sync with the language version also matter.

Google Closure does a pretty good job at stripping unused code. ClojureScript is powered by Google Closure. Leiningen gets you package management.

Long ago (in a company far away!) I evaluated Lua vs Python for use in a simulation engine as a scripting language for extending behaviors of stuff. Lua won, because of the easy integration with C and it's general lightness. However, I prefer Python. The big reason I like Python is that it doesn't try too much to be elegant, and happily creates specific functions for specific usages. I was able to get more done, more quickly in Python, even though I, from an aesthetic standpoint, though Lua was ... neater.

I've since used Lua on numerous occasions, and I still love it. But I always reach to Python because of it's pure, raw usability.

    Lua is a great language

  1. Arrays start at 1
  2. "begin" & "end" instead of {}
  3. No ++, +=, etc.
  4. No bitwise operators
  5. No "continue" statement
  6. Uses "~=" instead of "!="
Those are just the ones I remembered off the top of my head. It goes against so many modern conventions that makes it a pain in the butt to work with.

    1.  array[0] is syntactic sugar for *(array+0).  0 is an offset from an address, not an index.  
        Lua has tables.  Tables have indexes.  Indexes start at 1.
    2.  a design goal was to be easy for new users, not just existing programmers.  
        begin/end was borrowed from SQL.
    3.  Less operators is less to learn.  New users are familiar with +, -, *, /.  Operators like ++, --, *=, 
        increase the learning curve.  x = x + 1 is not hard to grasp.
    4.  No bitwise operators because no integers.  numbers are 
    5.  continue statement is not necessary, adds more
    6.  Why should it be '!=' and not '<>'

I'm not sure it goes against modern conventions, but it does go against a few C conventions. Not all languages need to be C like.


  Indexes start at 1.
That's your opinion.

2, 3.

Using "it's easier for new users" is not a good argument for a good programming language.

  x = x + 1 is not hard to grasp.
It's not, but it's more typing. I would argue that ++x and x += aren't that hard to grasp.

4. Javascript has bitwise operators even though it only has doubles. It simply casts it to an int32 before doing the operation


Continue is very much necessary in loops like this, unless you want some deeply nested code:

    	if(...) continue;
	if(...) continue;
    	if(...) continue;
    	if(...) continue;
    	if(...) continue;
6. It's what the most popular languages out there use. It doesn't have to be !=, which is why it's towards the end of my list, but being different than everyone else is even worse for new users, which is the opposite of what you're arguing for.


Garry Newman blogged a bit about this too. His game used Lua extensively, but he changed his opinion about it: https://garry.tv/2014/08/16/i-fell-out-of-love-with-lua/.

Now, I don't like JavaScript, but at the moment, it's the best scripting language for embedding out there. I have embedded both lua and v8, and I consider v8 simpler in terms of getting it to work with your code.

1. Indexes start at 1.

It's not my opinion. In any area other than programming, the term index has a specific meaning and indexes start at one. The term used in C for arrays should be called an offset, not an index. It's the wrong word. We called 2^10 a kilobyte too, even though kilo means 10^3.

  for ... do
    if ... then goto continue end
    if ... then goto continue end

> Operators like ++, --, *= increase the learning curve. x = x + 1 is not hard to grasp

why the heck not have both? x += y is more elegant than x = x+y. I understand not including ++ (Python) to not enforce 1 as a standard value, but it also doesn't seem to help anyone.

1. Tables in Lua have keys. Any value other than `nil` may be used as a key, including `0` if you so desire.

2. Lua's usage of `begin`/`end` was borrowed from "Modula" according to the HOPL paper[1]. Probably Modula-2, but the timeframe would not exclude Modula-3 as a possibility, though I suppose it doesn't really matter.

3. I find this to be a poor argument, but I've never felt like I was missing out on a whole lot by not having modification operators in Lua, either. It's not something that comes up often enough that the couple extra keystrokes should be a deal-breaker.

4. Lua added integers alongside flonums in 5.3, along with bitwise operators, but it's been possible to configure Lua's number representation at compile time for as long as I can remember. Prior to 5.3, anyone with a usecase that really necessitated bitwise operations could easily add them with a dozen lines of C code, or they could use LuaJIT which has offered bitops for some time.

I agree completely with points 5 and 6; nothing to add there :)

[1]: https://www.lua.org/doc/hopl.pdf

Syntactical issues are not so bad IMO, default scoping and version incompatibility between 5.1/5.2/5.3 are much bigger.

Also the barebones standard library wouldn't be much of an issue if there would exist a stable ecosystem, LuaRocks is one attempt but fragile at best.

Shouldn't you start your list at index 0? ;)

You got me :P

it's funny your numbered list of reasons lua's bad starts with the fact that array indices start at one by convention in lua, and the index for that first complaint is 1.

English is the worst programming language :)

Was Lua ever in competition with Python? Lua was for embedded systems and integrated as a scripting platform for other products. That's a vastly different market and design purpose than a general-purpose scripting language like Python. What has Python actual won over Lua that any other language hasn't won?

I'm actually surprised JavaScript hasn't surpassed Lua for the kind of things Lua is designed for -- but Lua seems to hold on and people aren't generally embedding JavaScript as the scripting language for other programs.

I started using JavaScript in my latest window manager after first using Lua. Since it’s for macOS the JavaScriptCore framework was already built in and uses the latest syntax. Honestly I prefer JS now even for embedding as long as it’s on macOS. Dunno how easy it is to embed on other systems.

Mongoose OS (iot OS) [0] uses embedded Javascript.

[0]: https://mongoose-os.com/docs/quickstart/using-javascript.htm...

I've seen far more game engines implement Lua as their scripting language compared to python, so I would hardly say it's 'lost' against python, at least for video games.

Video games is where Lua started to gain attention in first place.

Instead of Lua, I'd like Scheme to have become a more popular language for all those reasons you mention.

Is there any flavor of lisp that is close to luajit performances and still small?

Gambit-C [0]. It's a R5RS Scheme, and is near-C level of performance. It compiles to C, so embedding it is fairly easy too. Compiles to static executables, which is easy for distribution.

Chez Scheme [1]. It's a R6RS Scheme, so bigger, but Chez has much better embedding support, is backed by Cisco instead of a single dev. Chez doesn't make standalone executables though, because Chez is jitted. It may be the fastest Scheme. It also includes a compiler, a profiler, a great debugger, live memory-introspection, and an enhanced REPL [2] that can dump out it's definitions and any comments into a lovely Scheme file.

[0] https://github.com/gambit/gambit

[1] https://github.com/cisco/ChezScheme

[2] https://cisco.github.io/ChezScheme/csug9.5/use.html#./use:h2

Chez is not JITted. Chez compiles to native binaries that run win the chez runtime (so not stand-alone).

> Source code is compiled on-the-fly when loaded from a source file or entered via the shell.[0]

> Whether compiling on the fly or precompiling, the compiler produces optimized machine code, with some optimization across separately compiled library boundaries. [0]

> Chez Scheme compiles source forms as it sees them to machine code before evaluating them, i.e., "just in time." [1]

Chez can both JIT and pre-compile. But it absolutely has a JIT, and has had as far back as I could trace.

[0] https://cisco.github.io/ChezScheme/

[1] https://www.scheme.com/csug8/use.html

Well, sure, but it is not what we mostly consider a JIT. The "JIT" part is just an AOT compile at runtime, without most of the nice things that we generally consider being a JIT.

Take a hypothetical example: (for ([i (in-range a b c)]) (display i)). At compile time, chez has no way to know whether c is positive or negative (if it is set at runtime), thus making each iteration check whether c is positive or negative. In racket such code has negligible performance impact because the runtime will generate native code "just in time" with optimizations deduced from run-time information.

The JIT of chez is just a side effect of the AOT being fast enough to run on the fly. SBCL does the same.

> Well, sure, but it is not what we mostly consider a JIT. The "JIT" part is just an AOT compile at runtime, without most of the nice things that we generally consider being a JIT.

Huh? Then what do you consider a JIT?

> In computing, just-in-time (JIT) compilation, also known as dynamic translation, is a way of executing computer code that involves compilation during execution of a program – at run time – rather than prior to execution. Most often, this consists of source code or more commonly bytecode translation to machine code, which is then executed directly. [0]

[0] https://en.wikipedia.org/wiki/Just-in-time_compilation

The Wikipedia page supports my view. Chez compiles all code prior to execution and does not do any dynamic code generation during runtime.

It is just fast enough that AOT compilation isn't a noticeable delay even at the repl.

I'm not understanding you.

Cisco says Chez compiles code just before it uses it, at runtime:

> Chez Scheme compiles source forms as it sees them to machine code before evaluating them, i.e., "just in time."

They refer to it as a "incremental native compiler". Incremental compilation happens at runtime. An incremental compiler that runs at runtime is a form of JIT compiler.

> JIT compilation is a combination of the two traditional approaches to translation to machine code – ahead-of-time compilation (AOT), and interpretation – and combines some advantages and drawbacks of both

Everything I know and Wiki says that is a JIT.

Are you saying it isn't a JIT because it isn't a tracing JIT? Bearing in mind tracing JITs came about in the 70s, but JITs themselves are about a decade older.

The incremental compilation step is just their name for the nanopass compilation (many small steps). The code you execute is static and won't change during the execution of the program (unless you redefine it using a repl).

Do we at least agree that Chez does not generate code at runtime? Because I am very certain it does not (I have spent a lot of time with the chez codebase)

If you still want to call it a JIT then every language that compiles code to an intermediate representation and then executes it is a JIT language,which means just about every friggin language.

I have also ported a non-trivial amount of code making use of rackets JIT optimizations (based on hot paths) to Chez and have spent a lot of time compensating for the lack of the same optimizations in Chez.

SBCL compiles code fast enough to not have a distinct compilation step for most software in a way similar to chez and no one would consider it a JIT.


Let me quote Andy Keep (akeep on github. One of chez schemes developers):

Chez Scheme does compile at the REPL, but in general I would not call it a JIT compiler. It just compiles everything the same way regardless of if you are compiling it ahead of time or while you are running in an interactive session.


Guile is the one I would embed, but it's speed is not yet on par with LuaJIT (and probably never will be, since LuaJIT is in a class of it's own). Guile3 will probably be one of the faster schemes since they are implementing native compilation, but that is still some years away.

It is nice to embed though, and is a lot faster than python for things that isn't only using python things that are implemented in c.

Chez scheme is extremely fast.

But a lot bigger than LuaJIT, and probably not as fast.

They're pretty comparable, performance wise, actually. Both are generally close to C speeds.

Not in my experince. optimized chez (optimize-level 3) where I have spent quite some time optimizing the code, is generally within 2-5x of optimized C.

I have found LuaJIT to be about that fast as well, but with quite a lot of exceptions where it is within 1.5x of C. I would say it is still the fastest dynamic language implementation.

The work done by the racket7 folks on Chez might change that though. I have been reading Gustavo Massas patches to chez, and he is a pretty bright guy :D

You can implement a lambda style of programming in lua. Im made a game for the spring engine, where i implemented a simple function to use anonymous functions. Granted it doesent have the lazy computation of a pure functional implementation.

T= process(T,

                if type(element) == "number" then 
                   return element

Hey, it looks like a lot of valid comments of yours are invisible to normal users because they (and you) are marked as 'dead' in the system. I've vouched for as many as seemed valid. You might want to message one of the mods to ask to be marked as undead, or something.

Do you have any insights on why Lua didn't win?

I think the very reason I like it is the reason it didn’t crossover to mainstream success - it is so minimal it couldn’t compete with the all encompassing standard python library.

NodeJS is an obvious counterpoint to my thesis - it’s minimal stdlib as augmented with tens of thousands of NPM packages to implement every feature. This is my preference, but maybe Lua was too early for this strategy to succeed. The vicious attacks NodeJS gets (read about the leftpad.js debacle) because of their philosophy shows that a lot of people disagree with me, but I like the small language augmented with a rich third-party ecosystem.

Python and NodeJS both beat Lua because they were scripting languages involved with web servers and web clients, and the massive influence of the web on the job market meant that everybody was going to want to learn Python (and Javascript) and then try to apply it blindly to everything.

It doesn't matter if either language might not be competitive with Lua or any other language in terms of design / features.

I would agree with this point in respect to Python, but not Node.

I think Node was in large successful because web developers already have to use JavaScript so the promise of unifying on one language and set of libraries is appealing to many.

OpenResty and Lapis are quite capable in the web space, but they haven’t seen widespread adoption.

What I had drawn from the leftpad event in regards to ecosystem was the improper usage of third-party libraries; depending too much on libraries without actually validating what work they were doing, and how well they were doing it, and the realization this was common behavior throughout the community

I don't recall anything particular to comment on small vs big std library. The above complaint would apply to either case

One big reason is language stability. Lua changes the language in backward-incompatible ways. It’s part of how they make the language so clean, but it makes it difficult to build an ecosystem around.

I'm not OP, but I couldn't get past the fact that arrays start at 1 rather than 0.

While many languages follow a 0-indexed convention, as you see here, not all do. Another couple of cases I run into are Postgres arrays, and switching between “first”, “second”, and “nth” in Clojure.

If it helps, you can think 0-indexed as referring to the offset, and 1-indexed as referring to the position. It’s a shame to dismiss the entire language due to what one might argue is a minor aspect.

I would not call Clojure 1-indexed though because of 'first', it uses 0-indexing for list, vectors, and arrays.

To be clear, I’m not calling Clojure 1-indexed: I provided “nth” and “first” as examples of contrasting 0- and 1-based conventions; in this case within the same language.

Clojure being a lisp also has different enough syntax it is easier to jump back and forth because you're doing way more context switching. Lua looks enough like other c-style languages you can forget it is 1 based and make all sorts of nasty mistakes.

You can certainly argue it is unfair, but it is still something that increases the risk of mistakes.

Replacing (first coll) by (nth 1 coll) in clojure happens to me more often than I'd like to admit

I mostly write in Java for work. Back to the good old high school days, I spent 2 years learning programming in Pascal (yes, Turbo Pascal for DOS!), so seeing arrays started at 1 is not something strange for me :)

Of course, in Pascal you actually can pick any number where the index starts/end.

Totally agree with you. Zero-indexed arrays are one of those language design dogmas that are simply set in stone, really awkward to switch between languages when Lua has such a fundamental difference of opinion.

They don't have to, since Lua tables are hash sets and you can use anything as a key. You can start them at 0 if you want. table[0] = "foo" is valid and works. The downside is you have to add '-1' in a few places, but it's not as many as you'd think. All of my Lua code uses zero based arrays. I think the only place I had to add -1 was initializing numeric for loops.

Since Lua “arrays” are just tables with numerical keys, couldn’t you just start at 0 if that’s your preference?

For me it was the fact that the array ends at the first 'nil' you store in it. 1-based arrays I could get used to; that was just maddening.

The lack of a large standard library and a decent package manager were key in my mind.

I really love the simple elegance of Lua, but the build everything yourself ecosystem made Python and Ruby naturally better choices for larger non embedded solutions.

These days the package management problem has been solved with LuaRocks, but many packages are unmaintained.

The language is so old that it’s place seems to be set and unlikely to change.

Perhaps there was not a race. Lua is a great language; Python is a great language. Both have a place in my tookkit. If you are into language design, Lua is a language worth studying and understanding.

It didnt say- oh, btw you can use js functions and packages as library in lua.


Lua and python don't really compare in my mind. Lua is much more of a glue language, i.e. extending the application via the C API. It is designed around extending application -- and itself.

My knowledge of Lua is limited, however, so could you elaborate on the comparison criteria? I mean I wouldn't personally compare Python and Javascript. To be honest I wouldn't compare Javascript with anything.

Python's design is around readability. It is interpreted, while Lua runs via a VM. I guess they are both extensible, although I don't have Lua experience in this area to compare. Would you care to add a bit more information?

Applications are open for YC Winter 2024

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