No way, that's too soon! I only recently started working on a MoonScript fork[1][2], I'm not ready for HN! ;) My use case is integration with Awesome WM, and I hope to bring MS up to modern standards (whatever they happen to be) in the process.
The current MoonScript is in a really tight spot: the aging codebase doesn't get updated, there's no one leading the development, and the community has like 20 members tops. We need people! For both developing the language, discussing its design, and testing the changes. Feel free to drop by and say hi on Discord: https://discord.gg/Y75ZXrD
Also, there's another effort aiming to modernize MS: a compiler written in C++: https://github.com/pigpigyyy/MoonPlus/ - it's new, but it already implements all of the MoonScript language - definitely worth taking a look at.
Thanks for your enthusiasm, but I just wanted to clarify that I'm still leading the development of the project. I'm happy to see other people's ideas about how the language could be different, but I'm going to be very conservative about any accepted changes. I have a lot of code running in production in MoonScript that I have no plans to rewrite, so I have no plans to make breaking changes to the language. I feel it has reached a level of stability with the syntax that I'm mostly happy with. I encourage you to try out other stuff with your fork though. Hope that explains.
For others, the discord linked above is the official one, so feel free to stop by.
I'm the creator of MoonScript. I code in MoonScript daily, it's an integral part of many of my projects, including the company I run. It's something I launched a very long time ago, it's been featured on HN quite a few times now!
I feel a little bad for threads like this: if you look at the github or the website, it hasn't seen any significant updates in quite some time. The reality is, though, it's reached a level of stability where I don't need to worry about it and I can work on using it to build other things. Although I have many ideas to fundamentally change it, introduce new operators, paradigms, etc., at the end of the day I value more that is has stayed pretty consistent. I have 100s of thousands of lines of MoonScript running in production environments. I'm more interested in refining the tooling & squashing bugs. I'm considering just bumping the version to 1.0 so people don't get confused about the viability of using it. If it's something you think fits your needs then go for it. I will continue to support it indefinitely because of how integrated it is into many of my projects.
The guy behind this language is Leaf, who also runs the indie gaming site http://itch.io. His twitter account is https://twitter.com/moonscript. Really cool guy and he often adds stuff to Itch after people request it on Twitter. Moonscript is what enables him and one other person to run one of the biggest indie sites on the web. (Small potatoes compared to Steam/Epic/Blizzard, but still much more impressive than that you might expect out of a two person team.)
After years of people saying things like they wish they had Lua in the browser instead of "awful" JS, I finally used Lua for the first time writing games with Pico8 (https://www.lexaloffle.com/pico-8.php).
I found Lua to be so bare-bones and even quirky that I yearned for the expressiveness of C. I was so unimpressed and miserable working with it that I wondered if these people in the wild who supposedly love Lua have used another programming language in the past 15 years.
The "bare-bones" nature of Lua is really nice when you need it, so I like that it's an option. But I would guess that most of the time you need Penlight [1], and I found middleclass [2] to be a pretty good OO library.
After that, just build some abstractions. It's very easy to build DSLs in Lua, or if that's a little too dynamic for you (it is for me), it's very easy to use patterns like Builder or use an OO library and build some class taxonomies.
Anyway, Lua's not batteries included in lots of ways. It does take a little time to build it up for your specific usage. But most users think that's a core feature.
Yes, exactly. There are many libraries of common helpers - I use Microlight, Moses, fun, and std; there's a lot of overlap, but they all have unique features, and there's no real cost to bundling them all, so I do just that.
As a Lua proponent, I'll say that it's simple, small and easy to embed in an application. That's worth a lot, and I do think there are solutions that are much easier to express in Lua than in e.g. C. Compared to JavaScript, there are very few surprises, which I think is where people wishing for its replacement are coming from.
My gripes with the language are probably common: no arithmetic-assignment operators like "+=", no real arrays, few array-like operations built-in, array-like tables' first index is 1, no integer type...
Maybe some of your frustrations come from the characteristics of the PICO-8 API and its development environment. It leans on you to implement a lot of basic game logic in a single file, and if you're editing within PICO-8, you only have a basic text editor with a 128x128 pixel looking glass view into that file. I find that Lua works best when you offload a lot of the gritty logic to the host application and use Lua mostly as a glue language, but in PICO-8 the opposite mostly seems to work to its advantage because it's more approachable to newbies with its small, obvious API surface and all-in-one development environment.
I can (obviously) only speak for myself, but the reason I use and like lua isn't because I particularly love lua-the-language; it's because lua's syntax/semantics/implementations are tiny. I can easily hold it in my head and build on top of it. Its simplicity is its strength.
Not sure I buy that argument. By that reasoning Code written in Go must be more complex than code written in Scala since the language is much less expressive. However in practice I haven’t found that to be the case at all.
That’s so interesting to me, I totally have had that experience specifically with Go! The lack of exceptions and genetics contribute greatly to Go’s simplicity but I have always felt like my code suffers for it.
Do you find that your Go code is, if not more complex, at least more verbose than in Scala?
If that were the case, would you also say that surely c++ code is simple and easy to hold in one's head, since c++ itself is comparatively large and complex?
That's interesting. The opposite is true for me, but I can see (in the specific circumstance of C vs C++, at least) how you would see it that way. For me to find C++ easier (or equally easy) to read or write than C, it has to be a very particular subset of C++.
Do you find it to be the case generally, that larger / more complex languages result in code that's easier to read and write? If so, are there many exceptions?
I think all else being equal, yes? C# is very complicated and LINQ is an extremely complicated language feature, but if I need to express something that fits well with LINQ it’s stunningly clear. ES2017 has destructuring and async/await, quite complicated compared to ES5 yet they make my logic easier to follow vs doing the same tasks without. Even if each individual expression or statement in ES5 is easier to understand than ES2017.
Of course, with larger languages you can also come up with weird examples and say “what do you think this does?” in a brain teaser sort of way, or play “spot the undefined behavior” in C++.
Exceptions? I guess if features are both hard to understand and mandatory, like monads in Haskell or structural typing in TypeScript (not saying they are bad features, just that they add to the cognitive overhead for me). Or if there are lots of features and they’re not orthogonal to each other, leading to lots of surprises. Or certainly if you’re working with people that are overly clever and you find it hard to understand their code.
LuaJIT is still one of the fastest VMs around and it's worthwhile to be used as a backend either by transpiling to Lua or compiling directly to LuaJIT bytecode. Here is a project which implemented both approaches: https://github.com/rochus-keller/Oberon
I'm a fan. It takes some getting used to, because things which are semantic features of other languages (anything that's an object, notably) is a construct in Lua.
But the entire language fits in your head, and it's a powerful and composable system. I use LuaJIT fwiw; after you get a taste of its FFI, it spoils you.
First-class environments, overriding of indexing and assignment, coroutines, there's a lot to love (not you, Wirth indexing and default globals).
1-based indexing I can live with. The fact that the "array length" operator stops at the first nil in the array is what makes Lua unusable for me.
Edit: 1-based indexing I can live with. The fact that the things you create with {a, b, c} act for the most part like arrays except for the fact that if they contain nils, the "length" operator is undefined and usually stops at the first nil, is what makes Lua unusable for me.
They are not arrays. There are no arrays in Lua. There's only one compound data type (excluding userdata) and that's table. Tables are hash maps, or dicts, not arrays. You can represent arrays with tables, but that would still be "tables acting somewhat like arrays" and not "arrays". Also, you cannot actually store a `nil` value in a table. Nils are what you get when you access nonexistent key; that means the iteration stops at the first n from a 1,2,3... sequence which has no value in the table. If you need to represent a "hole" in the array, use a placeholder object, then convert it to nil at the point of use.
There are many libraries of common utilities that make working with tables-as-arrays more convenient. If you don't want to use them, it's mostly trivial to write them yourself. What you shouldn't do, though, is trying to use the raw tables as arrays: that could indeed be a frustrating experience.
That's a really common misconception. If there is a "hole" in the table, the result from the length operator is undefined. It doesn't always stop at the first instance of nil.
Tracking the table length manually and stashing it in a key (usually "n") is something you see pretty often.
^this, to me is hard to imagine. i’ve taken it for granted that i didnt need to track the lengths of my list-like data structures myself. at the least checking the length of the list of dictionary keys via some builtin.
--- get all packed entries, until gap
-- func on the provided list
-- @param list table to be inspected
-- @param func do func what you func want
table.each = function(list, func)
for i,v in ipairs(list) do
func(v, i)
end
end
--- get all entries, packed or not, until completion
-- func on the provided list, completely
-- @param list table to be inspected
-- @param func do func what you func want
table.all = function(list, func)
if (list ~= nil) then
for i,v in pairs(list) do
func(v, i)
end
end
end
As long as you don't skip a numeric key, you can rely on the length operator for "array-like" tables. There's no built-in way to check the length of a "dictionary-like" table, but you can overload the length operator in Lua >= 5.2 or LuaJIT.
The base language is very simple, but you usually have the building blocks necessary to add the behavior you're missing.
Lua has many lengths. From string lengths to table lengths, from number
lengths to sequence lengths, from sequence lengths to proper sequence
lengths. They are the many lengths of Lua.
The # operator returns string lengths and table lengths. It is the
standard length operator, and it's what you usually use to get the
length of an object.
The string.len() function returns string lengths. It typechecks the
argument to make sure it's a string, but otherwise returns the same
value as #.
There are various types of table length. Some of them are sequences,
some of them are not. Some have nothing to do with length, but rather
with count.
The simplest length is the one provided by ipairs(). It only iterates
the "proper sequence" part of a table. That is, it iterates the set of
contiguous positive integer keys of a table.[1]
When in doubt, this is the length you should rely on. Don't do `for
i=1,#t`, but instead use `for i,v in ipairs(t)`.
Another simple length is the one provided by pairs(). This is actually a
count. If for every iteration of pairs() you increment a counter, you'll
end up with the number of keys in a table. It is rarely used, but can be
useful sometimes.
If you want a manual table length, the simplest way to do it is probably
to just use an `n` field. While Lua supports this usage, the standard
library doesn't, so you have to deal with it manually. While the
standard library doesn't natively support the `n` field, some functions,
such as table.pack(), may emit it.
Another length option is the highest key of a table. You can get this
length by combining pairs() and m ath.max(). It can be useful in some
niche applications but it's quite slow, so consider a manual length (see
previous paragraph) instead.
By combining pairs() with type(), you can get the number of non-integer
keys in a table. While this count does exist, I have never seen it used
in practice.
The length operator, #, can also be applied to tables. If your table has
a positive integer key, and there's a smaller possitive integer that is
not a key (i.e. the value associated with it is `nil`), then this
shouldn't be used. When using a table without manual length, this
operator is usually faster than any other method[2], but it does have
the aforementioned drawback. This table length is the only table length
that is unspecified for some tables.
Finally, you can also use your own length algorithm. Use this if you
want fast runtime, but the drawbacks of the length operator make it
unsuitable for your use-case.
And these are the many lengths of Lua!
[1] - I'm not sure how many people know this, but this property
(stopping on first `nil`) is actually described in the manual. That is,
ipairs() on a table with "holes" is actually well-defined.
[2] - The Lua manual doesn't guarantee O(log n) time complexity for the
length operator. If you want guaranteed O(log n) runtime, use your own
length algorithm. This means # could have O(n) time complexity (i.e.
equivalent to ipairs()), or even O(m) where m = the number of keys in
the table (i.e. equivalent to pairs() + math.max()).
Exactly. Lua is designed for close integration with C. This is made much harder by having such a fundamental operation work differently between the two languages.
I really wish someone did a luajit fork with zero based indexing. It's even more galling in Luajit, because you can otherwise use native C types completely frictionlessly.
I'm on it... sometime. Some people have told me "That would be bad because you would lose all these awesome Lua libraries" but I think it's not such a big problem because Lua has no libraries, the FFI will still work, C will still be 0-indexed, and it's not too much work to convert a Lua library to use 0-based indexing.
I 100% agree. Lua is a small and elegant language, with two unambiguous severe flaws: global scope as default and one based indexing (one based indexing might be fine in other contexts, but not as some C/C++-glue language). The global scope thing can be mostly worked around with tooling, but the 1-based indexing (which in the context of luajit's seamless FFI and terra really means both 1 and 0-based indexing) adds so much friction that it's well worth giving up on or patching existing lua libraries.
Additionally, the lua ecosystem is already fractured (luajit is 5.1 + some selective ports of 5.2 stuff) and my feeling is that making this fracture complete will probably work out better in the longer run.
I know that luajit is carefully written to make this fast as well, but it's of course nonsense that nothing prevents one from doing that. It breaks `#` for starters:
#{[0]=1,2,3} == #{2,3} -- true
To be able to use zero-based indices ergonomically, the standard library and so one need adjusting as well.
Yes. I looked into consistently using [0] in the past, overriding `__len` and `__ipairs` etc, but kept running into corner cases that stopped it working robustly.
Despite how it may appear, 1-based indexing is quite fundamental to Lua.
I have worked professionally with Java, C#, C++, Javascript, Python, PHP and Lua. Guess which is my favorite language? :) I think I might prefer something like Kotlin or a good lisp to Lua but I have not used them as extensively. Lua is just so consistent and so powerful. Coming back to it after a year off is like coming home. Compared to javascript and python it's behavior has not suprised me once. Closures, variable scope, everything just makes sense, always. I have not googled syntax in years. Once in a while I look at the tiny reference doc. That said I would not write certain things in lua because the ecosystem is very lacking.
That was my experience with it too. I read the "Programming in Lua" book from cover to cover 10 years ago, and it remains one of my favorite programming books. There's a lot of great information in that book (not just about Lua).
And I read a lot of their academic papers which taught me a lot about PL design and implementation.
However whenever I try to program in Lua it feels both impoverished and verbose compared to Python. People complain about runtime errors in Python but Lua has even more of them. And the libraries are significantly worse for the same task.
It's not that JS is awful in itself, it's more that the combination of HTML, JS and CSS is not optimal for creating applications, especially when compared with some desktop options.
I found the luarocks eco-system to be quite typically of web module software. Fractured between versions, incompatible and constantly requiring intervention to work.
I do not blame them though- the language has gone through several 2.7 ~ 3.0 years of the snake shisms.
PS: Looked around, and it turns out - there is even a lua implementation for the javascript vm..
Using luaver and luarocks has been a pretty great experience for the last several years, except that there is often no library at all to do what you want. In this case I usually use a C library and the FFI.
Pico8 is hardly representative of what's great about Lua. Its a fantasy console, designed to be limited.
If you want to understand the Lua love, check out some of the other game frameworks like Cocos2D, MOAI, or Godot ..
I've used pretty much every language under the sun, but I keep coming back to Lua as my first choice for scripting because its so efficient, and easy to use. There really isn't much that can't be done with Lua and metatables. I prefer it over python because python dependencies are hell, whereas luarocks is a dream by comparison .. plus, libffi is just so darned useful ..
I love moonscript, it makes lua feel so much more usable for me. Admittedly, I'm mostly only using it for convenience in configuring apps(such as my WM¹/video player²/etc).
If you want to see a large-ish example of what you can do with moonscript the howl editor³ is a great example. It is also a nice extensible editor in its own right, including a very usable vi mode.
No particularly good reason. I have no use for the vlc gui, so mpv is fine for me. At a push I'd perhaps say the lighter dependency stack is a plus, but if mpv didn't exist I'd probably install vlc without thinking about it. FWIW, apt wants me to install 36 new packages for vlc even with --no-install-recommends.
edit: Or - I guess - some other scriptable media player, as being extensible is the thing that matters to me. Unless someone magically matches my quirks with their non-scriptable player ;)
I love everything about MoonScript except for that damn backslash. I think it's much nicer to use than Lua, but it still bugs me, bikeshedding be damned. The asymmetry, the fact that no other language (that I know of) uses it for a similar purpose, just the look of it in between text...
I once came close to trying to create a near-identical fork of the project which changed the backslash to something else, but I figured it'd be pretty pointless. I know I'm definitely not the only one who dislikes it, at least.
I also hate it (along with HSP in general). Anything that exists outside the grain of programming language conventions so baked in as basic arithmetic operators throws me off.
Being a J programmer [1], the order or placement of ASCII characters does not bother me, so I think it's about familiarity, habit and ability to see past the syntax.
> I know I'm definitely not the only one who dislikes it, at least.
You're not. In my fork, I plan to replace `\` with `:` and `:` with `=`, so it's closer to Lua, and less irritating to look at. I need to figure out how to code that while retaining some backward compatibility, but "fixing" this is one of the major goals of my fork.
Efficient, expressive, elegant
Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.
Am I the only person who finds "nim is python-y" an odd description? I often think that even "perl with off-side rule" would make more sense, given the variety of ways you can express many concepts in nim(TWMTOWTDI) and number of sigils.
I get that the stdlib is clearly inspired by python¹, and that using a reST-ish subset for documentation is Python-y. Beyond that surface scratch the comparison just falls apart, and often seems to end up as a pointless sticking point when introducing co-workers to nim. You can't even ignore the idea, as the moment they crack the awesome Nim in Action book or the official docs they're immediately shown it.
1. down to being able to just guess module names in a few circumstances.
> nothing at all about it is perlish that I can think of
Yeah, I could probably have picked a better example or more clearly expressed my thoughts. I was attempting to make a point about concepts such as the uniform function call syntax feeling like a perl-style TWMTOWTDI feature to me.
[The crux was supposed to be about how quickly just seeing the Python comparison in the docs ends up in a rabbit hole discussion. I didn't help with my own poor comparison.]
As a big fan of Nim, totally agree. I think the best description of Nim is a Pascal/Oberon/Modula language, with Lisp inspiration, and a Pythonic indentation/standard library. I think the Python influence is overstated because Python is such a popular language.
Most of it is. You can do a lot without touching any unsafe parts. It compiles down to JavaScript (which is memory safe), or C / C++ / Objective-C (which aren't).
C FFI is unsafe, as well as taking an address of something; but you rarely need these things unless you are already interfacing with unsafe C code.
It does not, however, delineate unsafe parts into an unsafe{} scope the way Rust does.
I love the idea of Moonscript but, is this weird to anyone else:
- class attributes are mutable and shared across all instances!?
- to give an instance its own state you gotta define attributes in the constructor
In that case, there's OOP modules for raw Lua that work well. I'd prefer to stick with Lua.
> class attributes are mutable and shared across all instances
an OOP module from Lua is likely going to work the same way. Most class based languages would behave the same (Python, Javascript are examples I can think of)
A "class attribute" is a value stored on the object that represents the class. This is a prototypical language, so all instances of a class share the same prototype associated with the class.
Regarding immutability, you could use metatable tricks or a library to enforce immutability on a Lua table. That's not something the language provides.
> to give an instance its own state you gotta define attributes in the constructor
So I believe the reason why you're making this distinction is because the prototypical inheritance and the explicit section that talks about this in the documentation. MoonScript lets you have any value be part of the prototype, not just methods/functions.
So I guess I'm writing all this to say that it's not weird, it's just the nature of prototype-based languages. I specifically call out this case in the docs to help people not get stuck.
Yes, the class stuff in MS is plain wrong, all of it (except `=>`, maybe). In my fork, I plan to drop it all. The thing is, the prototypal, copy-on-write nature of Lua object orientation is not a disability, there's no need to "fix" it, the model is powerful enough as it is, it just needs to be used well.
Some years ago I learned Io - another prototype-based language, similar to Self - and I was amazed at how expressive it is based on a very small set of core primitives. All the primitives, including async calls in coroutines, are either built-in in Lua or are trivially implemented with a small code transformation.
There's nothing wrong about it, I'm sorry you don't like it! I'm happy that you have your own ideas but to go around saying it's wrong isn't very cool. The system was very intentionally designed. Now that's it's been 9 years and I've written 100s of thousands of lines of MoonScript, the class has proved to be very reliable. It compiles to simple concepts that are easy to reason about when working with both large and small code bases.
> The thing is, the prototypal, copy-on-write nature of Lua object orientation
This line is confusing to me. There is nothing about lua that is fundamentally copy-on-write. That would have to be a choice the developer makes, but it would be hard to enforce with language primitives unless you're hiding data within meta-tables
I think you're getting hung up on details that have nothing to do with MoonScript: if you want a different language then use a different language. Don't go around saying it's wrong because it's not the language you want.
MoonScript reminds me of CoffeeScript in this regard. Lua and JS are both prototypical languages, which is a feature, not a bug. Putting some `class` syntactic sugar around it helps initially, but also causes confusion when it doesn't act like a traditional OOP class.
As an embeddable language, does MoonScript’s significant indentation cause any problems? I imagine it could be difficult to include MoonScript code inline with C code while preserving the indentation.
Is that so? Or is inline MoonScript code within C files not common?
My strategy is typically store MoonScript in separate files, then have build process that generates Lua and bundles that inside of whatever else. I wouldn't typically put it in a C file, but I might have the build system generate a hex encoded string as a header file. (This is actually how I build the MoonScript source into a single exe for Windows builds)
I recommend doing the MoonScript compile time at program build time to avoid any unnecessary compilation during runtime.
Thanks. I wonder if this would be any different if the VM executed MoonScript code directly, rather than first requiring translation to Lua.
I’ve been thinking about the syntax of an embeddable language myself, and have ruled out significant indentation because of the difficulty of writing such code inline inside C files. If that’s not a common use case, maybe I should reconsider...
There is no MoonScript VM, if you use the "execute moonscript" function provided by the moonscript library it internally compiles the moon code to lua, loads the lua code, then runs the lua code.
This means that MoonScript compiled ahead of time will have the same exact result as running it on the fly.
I used this playing with a Lapis project years ago. It very much fit the zeitgeist in the wake CoffeeScript's popularity. Although usable, it feels a bit dated now and a product of its time. The zeitgeist now requires static typing.
Fashion only cycles on equivalent choices. Some stuff are better or worse than the alternatives, so they stick either in use or out of it.
Types are one of those things that will stick in use. We will also probably see a wild diversity of type systems in experimental languages, as there is very likely one better than everything we use today.
> Fashion only cycles on equivalent choices. Some stuff are better or worse than the alternatives, so they stick either in use or out of it.
Even if that is true, there are potentially a lot of dimensions to consider in solving the problem of what is better or worse. Some of these dimensions, or where in that dimension what-is-better falls, take a long time to discover and are different for different problem spaces. Sometimes you think you've got it right only to learn that you were wrong much later.
But it isn't true. Fashion is by any indication only loosely concerned with utility and sometimes settles on downright masochistic flavors-of-the-day.
Yes, you can - I use it with Awesome WM, and with OpenResty/Nginx, and it works fine. MS compiles to pretty straightforward Lua and doesn't include any code that would make it hard to run as an embedded scripting language.
> Because it compiles right into Lua code, it is completely compatible with alternative Lua implementations like LuaJIT, and it is also compatible with all existing Lua code and libraries.
It would seem that there is no reason you couldn't.
LuaJIT is considerably faster in pure CPU benchmarks than JavaScript. It is also faster than C in some cases unless you are hand optimizing your assembly.
Depends what sort of code you’re running. LuaJIT is a tracing JIT - it’s great for code which runs the same way over-and-over, but has trouble with unpredictable branches. V8 does a much better job than LuaJIT for things like a loop containing an if/else with a 50:50 condition.
Overall, the fastest dynamic language JITs are probably V8 and other JavaScript runtimes - it’s worth noting that none of these use LuaJIT-style tracing.
That and the quad-color garbage collector would ensure LuaJIT's dominance for many years to come. The quad-color is well described, and someone might successfully implement it at some point; I doubt there's another human alive who could implement hyperblock scheduling. I'd love to be wrong.
The current MoonScript is in a really tight spot: the aging codebase doesn't get updated, there's no one leading the development, and the community has like 20 members tops. We need people! For both developing the language, discussing its design, and testing the changes. Feel free to drop by and say hi on Discord: https://discord.gg/Y75ZXrD
Also, there's another effort aiming to modernize MS: a compiler written in C++: https://github.com/pigpigyyy/MoonPlus/ - it's new, but it already implements all of the MoonScript language - definitely worth taking a look at.
[1] It started when I tried to add some FP conveniences to MS: https://github.com/leafo/moonscript/issues/407
[2] https://github.com/piotrklibert/AwesomeScript - nothing interesting yet, just a few ideas in the issues