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.
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?
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.
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.
Quality, available documentation, guaratee of supporting every OS targeted by the language toolchain, and future updates in sync with the language version also matter.
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.
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
floats.
5. continue statement is not necessary, adds more
complexity.
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.
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.
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.
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.
> 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 :)
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.
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.
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.
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.
> 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.
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]
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.
Edit:
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.
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,
function(element)
if type(element) == "number" then
return element
end
end
)
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.
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.
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.
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.
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.
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.
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?
I looked at love for teaching school children. It looked great, except they unfortunately use some sexual names for many of the most common libraries (like anal and lube). Highly amusing I'm sure, but I'm afraid not safe for children!
You don't have to give all the context to the children. And if they're not already old enough to understand what "anal" means, you can just say it's a butt joke (or maybe just leave them to their ignorant innocence, they can just believe it's a made up name for a time). If they are old enough, I'm afraid avoiding the word is going to save them.
"Lube" is used in non-sexual context. You can escape the puritan "inappropriateness" by referring to mechanics.
Also, how about replacing all sexual names by something else? A little sed script should be enough, right?
In practice, modern teenagers are going to go to the love wiki to find libraries and documentation, that's the main place to find it. There is no point me giving them an incompatible variant, which won't let them take their programs home.
Also, it's easy to say "oh kids, just tell them it's a joke", but in today's society, that is the kind of thing which could easily get one fired. If just one library was named sexually, it would be easy to say "oh, you are misreading that", but the pattern is obvious.
The problem here is that even if the teacher does as you say, guess what will happen when the parents hear about it. If you guessed, "be really, really mad at the teacher and yell at the school administration", congrats, you win.
Agreed -- and have seen it in action. It only takes a few vocal outraged parents to incite a riot, and shut everything down. Or get the teacher suspended.
It's illogical, but emotion often trumps logic and rationality.
I recommend using another game-engine then- that uses lua.
Unfortunately- the illusion that you can present a clean environment will be shattered rather swift, once one of them goes out to research for documentation and ends in any dev irc-channel.
These guys can live on code, hentai and ramen alone for months- and that is not a clichee.
My suggestion is to introduce a safe version of code and documentation via a repository and offer to get them any library they via that central repo.
Yes its more preparation work, but its going to keep the project alive.
TIC-80 is a free Pico 8 clone, with slightly higher resolution, and Android support as well[0]. It also supports moonscript. The "Play" section has some really impressive stuff[1]
None of those seem to exist in the standard libraries this package provides. Now one can (or has already) presumably put libraries with sensitive names in CPAN or PyPI or Debian Archives (e.g. libmoosex-, libsexy-, thefuck) or whatnot. Is Debian now an 18+ OS?
Aside, how does one archive/move/transform github issues? I like the low-friction aspect of blog posts as issues, but they aren't first class WRT git. The content is locked into the github ecosystem, not replicated/cloned/backedup.
My first "real" game[0] was made in lua and love. It was a great experience and I loved the community (pun intended). Godot engine has got me now, but I always keep an eye on lua and love.
One thing that also made me laugh a lot (despite some people labeling it as unprofessional) is the funny love-related names people give to libraries. Stuff like: HUMP, Gspot, flUIds, Polygami, Swinger, Love Bone, etc. Such a funny community :D
As a long time "Lover" I was delighted when I discovered that repo long ago. I starred it and forgot about it. Nice to see you finished it.
I always wondered how to make an open source game but still sell it. This seems like a plausible way to do. Hope you have a decent income from it!
That said, Love is a really great framework and I always wish there was something like it for Go. While I like Lua, I prefer compiled languages and I think Go could be nice for games but it is still lacking in that domain.
Hey thanks for the comment. In fact I know raylib. I made some simple particle library in C for it.
I think raylib is really great. It is very comparable to Love in greatness :-) and I keep recommending it to people.
However the go bindings have one problem (all go bindings for game frameworks i know of have this problem): it is not so easy/impossible to leverage the build target capabilities of raylib. E.g. the web target will just not work at the moment and the mobile targets will also involve a lot of fiddling.
It is cool to play around with but if you really want to make a game and deploy it to many targets none of the current go "solutions" is great. Maybe "ebiten" is good in this regard but there are some other showstoppers for me (no shaders.. etc.)
I'm trying to get in to game dev as a hobby and Love/Lua is great for someone who wants to make games, but would rather spend time writing code than learning something like Unity. Lua is simple and very easy to pick up, although I think the next time I touch Love and Lua I'll try out the OOP library the author mentions instead of rolling my own classes.
Other things on my list to try are Phaser for the easy cross platform distribution (it's JavaScript) and Godot, because although I enjoy coding little games "from scratch" I feel like I would be much more productive with the tools that Godot or Unity offer.
I’m in a similar boat. No experience with Unity or game dev in general, ran through some tutorials but it seems really heavy for doing something simple. I coincidentally picked up Lua recently and found it a delight, and this framework looks right up my alley.
I never looked into it before, but their basic game loop is flawed. It would work fine on Windows, but Linux and Mac have high resolution system timers and SDL_Delay(1) which is called underneath would really sleep 1ms (or maybe 2-3ms, but rarely more). This means that the game would run at 1000 fps and consume much more system resources than needed.
A much better approach would be to calculate how much extra time we have and then sleep that long (optionally minus 1ms to allow for that rare case where you get 2ms sleep). This can be #ifdef'd for non-Windows platforms only.
"This means that the game would run at 1000 fps and consume much more system resources than needed."
I don't really see why this would be a problem for a game which, while running, is generally the most important process for the end user anyway. Of course for services and such background tasks this would be intolerable.
You're right that it is often the most important thing - but not always. For example, people (myself included) often have a YouTube video playing in the background, which also consumes a lot of resources. Having both may start to cause problems.
Regardless it is a good thing to avoid over-computing. It helps those with lower powered computers or people on battery-powered machines.
I am currently writing a little game using vanilla JS and HTML5 Canvas. I plan on porting it to other platforms once I have a version of the game that is sufficiently complete and that is fun to play.
I haven’t thought much about what languages to use for the ports yet but maybe I will give Lua a try.
I want to target iOS first. Personally I am excited to port it to iOS but not so excited about Android because I want to try Swift and because I have an iPhone whereas my Android devices were all disappointing and I don’t like Java.
According to https://love2d.org/wiki/Game_Distribution it is possible to target both iOS and Android with Lua + Löve. If it turns out to be comfortable to work with and sufficiently performant I would prefer using Lua and Löve to target both of iOS and Android instead of Swift and Java respectively.
Has anyone here had experience writing for iOS or Android with Lua + Löve? Did it work well both in terms of tooling and coding and performance of the finished game?
Wow, this is fantastic and I just realized Github has topics. Anyone have recommendations of other really well done complete tutorials on https://github.com/topics/tutorial ?
It does. There are Lua and Love2D extensions. I use Atom when working with Love2D, however. The tooling there is better. BTW, there’s a great short Love2D course on Udemy.
It's not a tutorial, per se, but Game Programming Patterns is a free online book that explores common design patterns in game development: http://gameprogrammingpatterns.com/
As a non-game developer, it's been one of the most invaluable resources I jump too when trying to optimize something.
I bought this book and finished it in a couple hours. It's fun to read and the examples are well-illustrated, but it doesn't go into very much depth on anything and its breadth isn't terribly wide either. I would say it's good for people who are learning programming in order to learn to make games, but people who are already programmers won't get much from it.
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.