Hacker News new | past | comments | ask | show | jobs | submit login
MoonScript, a programmer friendly language that compiles to Lua (moonscript.org)
391 points by type0 on May 29, 2017 | hide | past | web | favorite | 162 comments



Hey all, I made MoonScript about 6 years ago.

I used it to build a ton of opensource stuff in addition to the company I founded. I use it every day and I'm very happy with how it's turned out. I regret not updating the language more frequently, but I've been busy building a bunch of stuff in it.

The biggest open source project is a web framework for Open Resty: https://github.com/leafo/lapis

It's used for the following open source websites: https://github.com/luarocks/luarocks-site https://github.com/leafo/streak.club

The company I made, https://itch.io also runs on it. There are a a ton of extra Lua modules I've made in MoonScript to facilitate it. Here's a list of my published modules: https://luarocks.org/modules/leafo

I've also completed Ludum Dare 11 times now, making a game each time in MoonScript. I've used https://love2d.org/ as the game engine. You can find the games on my GitHub: https://github.com/leafo?tab=repositories

Feel free to ask any questions


What's it like being a resident bad@$$?

In all seriousness, what's it like running a company using a language you built or is there only a couple of other employees? Also, I guess you're not worrying about bitrot as you can maintain it yourself and it doesn't need anything other than lua to run correct? Also, why did you originally pick lua. I'm gonna stop and read the articles now :).


Are you aware that TIC-80 supports moonscript? It's one of the better PICO-8 "clones" out there.

https://nesbox.itch.io/tic

https://github.com/nesbox/tic.computer/wiki#cartridge-metada...


I am, and I'm pretty exited about it. I haven't had a chance to work with it yet but I might mess around with it next ludum dare.


Is that open source? I'm wondering if it uses SDL or a similar library - binaries look pretty small.


No source code, but looking at the Windows binary it's using a statically linked copy of SDL 2.

Plus that file's about 2 MB, so really not surprisingly small.


A similar project, uses SDL, is open source, and came from antirez (of redis fame), LOAD81:

    http://github.com/antirez/load81.git


The font you are using looks broken to me. (Win 10, Firefox)

At 100% scaling, every "i" clearly looks like a "1". It reads "MoonScr1pt" for me: https://i.imgur.com/Ti12E5I.png

If I zoom in or out in the browser the i looks ok again.


Exactly the same issue for me too. My configuration is Win 8.1 and the Firefox 53. I should add that only the font used to represent code has this issue. Other fonts are okay.


Same for me as well: http://i.imgur.com/U0Cl2Sq.png

Win 10, Chrome 58


I don't see that at all. Maybe you found a bug for Win10/FF? I am on Win7/FF.


Dude, this is neat. Itch.io is a great site, I've bought a few things through it.

I like how your HN profile page mentions only MoonScript and not itch.io, though, in that the former is an open source project and the latter is a company :)


He's a very modest dude, so I make sure to advertise for him on HN whenever the chance arises.

I've been following him since the early days of itch.io and the progress he's made with it is astounding.


I was just looking at love2d, I don't know much about it. My son is 8 and is very bored with Scratch. A once over the documentation makes me think love2d might be a good fit for a next step from Scratch. How is the love2d community and is it active?


The love2d community is definitely still active, especially on IRC. Some of the nicest folks I've ever spoken to over the internet.

I can't recommend the language or its community highly enough.


A couple of friends just successfully launched a commercial game on android/ios/steam using love2d, and they're really happy with the results.

I couldn't believe it was made with lua, it runs really smooth.

http://midipixel.com/warlockstower/


The love2d source code is in C++. I believe Lua interpreter was embedded as a scripting mechanism

https://bitbucket.org/rude/love/src


AFAIK, LÖVE integrates LuaJIT.


Why wouldn't it run smooth if it was made with Lua?


I learned lua and love2d last year and found them ... lovely! Hadn't done any graphics programming in about 20 years, and thoroughly enjoyed the process. What was really great was the ability to target multiple platforms. I even wrote an SVG library @ https://github.com/globalcitizen/svglover and a roguelike @ https://github.com/globalcitizen/zomia for the 2016 Annual Roguelike Release Party (ARRP).


love2d is great. I've dabbled with a bunch of game engines and am an avid lover of Processing and p5.js, both of which are great for learning to code. Love2d is right up there with them, though the documentation is less newbie-oriented, IMO. The best thing for you to do would be to go through this tutorial yourself and then decide if you think it would be appropriate for your kid. http://sheepolution.com/learn/book/contents


For a 8 year old, Löve might be a bit hard.

At our weekly CoderDojo in Zurich, we're using love2d (and processing) as the next step for the kids that over 10 years and who are comfortable with typing.

Both just work!

https://www.meetup.com/Coder-Dojo-Zurich/

Your welcome to join with your kids or as a mentor if you happen to be not too far away...


Processing may also be a good choice.


Woah, thanks so much for itch.io - I'm a big fan of indie games and your site is amazing.


Thank you for Moonscript!

> The biggest open source project is a web framework for Open Resty: https://github.com/leafo/lapis

Looks like lapis is ~15K lines. The Howl editor has ~58K lines (including bindings and tests) - making it bigger, technically ;)


Hi, I made the far back side of MoonScript named ACPUL 5 years ago. I built it from the ground: high performance VM and expression based CPU assembly. It's growth up from small new language to high end gamedev mobile OS.

Images: http://acpul.tumblr.com/

Code sample: https://github.com/d08ble/acpul-demo/tree/master/dota

Video: https://www.youtube.com/watch?v=CAcv12eBqcc


I have to say that if I see last update in changelog from few years ago I assume the project is dead and one should not use it.


You should disabuse yourself of this notion, a lot of mature projects don't get updates because they don't have (known) bugs or are feature complete.


I don't do this with everything, it depends on what the project is and what my use of it would be. But with libraries or anything that would be a part of something you might find it won't work with your next OS update, browser update (js libraries), or it requires dependencies that are obsolete. I think it's a fair and quick filter.


If you were to devote more time to MoonScript, what would be the likelihood of including Lua 5.3 compatibility?


This post was timely for me as I'm working on a game project that could benefit greatly from each of the projects you mentioned. Thanks!


That's a lot of work you've done alone :)


Itch.io looks pretty nice except for the scrollbar on the left, I suggest you to add this in the css to make it look nicer on webkit browsers: https://gist.github.com/Ivanca/95e171e3d575001dd17787ec490c6...


I'm thinking of creating a blackbox site building service in lua and store people's code in db.


This is one of @leafo's excellent projects. He has developed a web framework for MoonScript called Lapis. One of the cool things about Lapis is that it runs on top of OpenResty, which is a high performance web server for Lua applications. Lapis is production-ready: it runs his amazing game marketplace, itch.io.


+1 for Lapis - it's fairly barebones, but it's also nice and simple. You can also code it in Lua, if you feel more comfortable with that.

I was looking into using it to make a site for hosting Love2D games which would optionally run right in the browser, because lua rocks, but I got distracted with other things.


On a related topic, Haxe now compiles to Lua as well : https://haxe.org/blog/hello-lua/ (disclosure : I'm the author of the lua target)

Back to moonscript/Lua, I've been super impressed with the YAGNI principals of the language. I was originally drawn to LuaJIT and its raw speed, but there's a lot of great things to say about the language and its community.

My goal is to write more Lua for smaller scripting purposes, and use Haxe/Lua for more complex projects, taking advantage of LuaJIT speed in both cases.


Thank you. The more I use Haxe the more I love it and I have compiled to Luna and it worked flawlessly. Thank you


It makes my day to hear this. Glad it is useful!


Lapis + MoonScript + OpenResty looks like a lot of fun, so I was just starting to get a local environment running for it and immediately ran into the Lua 5.1 and 5.2+ divergence. For someone who has just been a casual observer of the Lua ecosystem over the years, can someone talk about the community's feelings towards that divergence? The OpenResty docs basically state it's not worth the effort to support anything but Lua 5.1, which at this point is 5 years old and no longer updated.

From someone on the outside that makes me hesitant to spend much effort investing in the ecosystem. Are the libraries fragmented across different versions too? Are there really no plans for an upgrade path, or does everyone just expect to use 5.1 forever?


For many libraries, it's pretty easy to write code that works across all the versions. For many differences it's not too difficult to implement code for maximum compatibility: http://leafo.net/guides/setfenv-in-lua52-and-above.html

For me personally, the advantages of luajit currently outweigh the desire to upgrade to newer versions of the language.

Regarding Lua 5.1 being old, I haven't really found it to be an issue.. Lua isn't like most scripting languages, it doesn't come with a huge standard library. It's effectively just the language and some primitives. So it ages quite well as is.


For people knowing Lua a bit longer, the "divergence" is nothing surprising, it's an explicit and very long standing design choice by Lua authors. Specifically, that's what allowed them to evolve Lua to the awesome language it is, by making breaking changes when they invented/converged on a notably better way to do something. Personally, I find it more appropriate to look at each new Lua version as a wholly new language, rather than a "new release of the same language". A new language, which "accidentally", as a bow to the user, is very, very similar to the "previous language" from the Lua family (to the point of usually being API and syntax compatible).

Actually, the changes between 5.1, 5.2, 5.3 are super small and smooth compared to what was done between e.g. 4.x and 5.x, where language syntax was changed. Between 5.x versions, with some effort you can make your code portable, if you wish so. (I'd say that's what one would expect when seeing the major digit unchanged in a version string.) There are many libraries which take care to. I'm not 100% sure about this one (i.e. haven't checked), but from the top of my head, based on the author's reputation, I'd blindly say https://github.com/stevedonovan/Penlight should be a nice example of a library portable between 5.x versions.

The proliferation of the 5.1 version is sometimes semi-jokingly seen as a "curse of success" - that it became the first version to really nail it so well, that it spread so wildly, and the newer versions bring small enough changes, that some people find it hard to justify any effort at all to upgrade. But it's also perfectly OK: Lua 5.1 is a great language already, and can simply be seen as a done/completed work.


The reason is that the _ENV feature added in 5.2 would significantly slow down LuaJIT if it were supported. It's possible that Luac and LuaJIT will re-converge when Lua 6.0 is released, but arguments about this between Mike Pall amd Roberto Ierusalimschy have been unproductive so far. Until then, LuaJIT compiles Lua 5.1 with support for a few 5.2 and 5.3 features, particularly goto.


You have any references to these arguments between the two gods?


Totally not a concrete link, sorry, but - in a video from one of Lua conferences, I've seen Roberto mention in some polite yet clear way that talks with Mike which they had had always come to a standstill on something - at least that's how I recall it. On the other hand, since 2015, Mike was looking to pass luajit development on to someone else - so with this, situation could possibly change in the end. But I'm not sure what's the current status of this search; I seem to recall CloudFlare was reported to be in talks, but I don't know how it all ended - or if it's still unresolved.


Why would `_ENV` slow down LuaJIT? It only offers a subset of the `setfenv()` functionality of Lua 5.1 that's implemented in LuaJIT...


It uses lexical scoping, so it probably would mess up traces.


Late reply, but so does setfenv:

    > function foo()return function()return a end end
    > =foo()()
    nil
    > a=5
    > =foo()()
    5
    > setfenv(foo,{a=6})

    > =foo()()
    6


Nah I'm talking about the _ENV variable itself being in lexical scope.


i have no idea what im talking about tho.


Ooooh, maybe this is relevant:

    > function foo()
    >> local a = _ENV
    >> local _ENV = a
    >> return {a=function() _ENV = {r=6} end, b = function() return r end}
    >> end
    > r = 5
    > x = foo()
    > x.b()
    > =x.b()
    5
    > return x.a()
    > return x.b()
    6
    >
vs

    > function foo()
        return {
          a = function() return setfenv(2, {r = 6}) end,
          b = function() return r end
        }
      end
    > x = foo()
    > = x.a()
    bad argument #1 to '?' (invalid level)
    stack traceback:
        [builtin#11]: at 0x0101faf2a0
        [C]: at 0x0101f4ea60

    > function foo()
        function bar()
          setfenv(2, {x=6})
        end
        bar()
        return function() return x end
      end
    > e = foo()
    > =e()
    6
I hadn't thought of that...


wat?


With setfenv, the environment definition is lexical by default, but if you change the environment of the parent after creating a child function, the environment of the child doesn't change. You must `setfenv` the child as well.

With _ENV, it is fully dynamic, and can be changed "at a distance".

Also:

    > function foo()
    >> local function bar() return a end
    >> setfenv(1,{a=4})
    >> return bar
    >> end
    > a = 9
    > =foo()()
    9


There is divergence, it's been a known issue in the community for years. I already had that slide in my 2013 "state of the Lua ecosystem" talk: http://files.catwell.info/presentations/2013-11-lua-workshop...

The views on the issue amongst the community are divided too. Personally, I have decided most of my new code won't support Lua versions older than 5.3. That means no LuaJIT. If I have to use OpenResty seriously again, I will fork and adapt it to support PUC Lua 5.3. In the meantime, I use Xavante and WSAPI for the little Web code I write in Lua.


I believe this is because LuaJIT is 5.1-only.


People expect to use luajit forever.


This is my problem as well. I just installed 5.3 on my system but it looks like nobody really supports 5.3. There really is no clear guidance on what to do - do I just not use the latest version of the language?


If you really need to use LuaJIT, then you best stick with Lua 5.1. Otherwise, use Lua 5.3---it supports 64-bit integers (unlike Lua 5.1/5.2, which only use floating point) and if you are doing any parsing of binary data, string.pack() and string.unpack() are worth using (Lua 5.3).

Yes, there are issues with some Lua modules not supporting Lua 5.3, but the major modules support it, and LuaRocks makes installing modules rather easy.


Well, in the context of this thread, MoonScript, Lapis, and OpenResty don't support 5.3.


I understand that people love programming in classes, but I really want something like a stripped-down ES6. In particular, I want:

1. Lists and objects/dicts only; objects have no prototypes at all; objects have no first-class methods, but they can have functions as member variables, and those functions can close over other data in the object

2. All function and variable declaration is anonymous (as a consequence of 1); if you want to define a schema for an object, build a function that takes certain arguments and returns an object with the corresponding structure.

3. Coroutines/fibers/etc for async things; no promise/callback hell

4. Type annotations a la Python (not sure how this fits with 2 just yet)

EDIT: I did a bad job of emphasizing this, but syntax is important to me--I want something that looks like:

    var foo = {
        x: [1, 2, 3],
        y: (a, b, c) => {
            var sum = a + b + c;
            sum * sum
        },
    }
Whether or not you agree that syntax should be a relevant criteria is a different matter. :)


This is basically Lua, excluding type annotations. Lua's form of OOP is very strange in that methods aren't bound to a parent context, but instead take the object as a value. For example, `object:method()` is the same as `object.method(object)`, and `function object:method()` is the same as `function object.method(self)` - the only difference being calling the function via `object:method()` is a bit more optimized. There is also a framework with networking, signals, and other cool features called `cqueues` which works based off of coroutines.s You might want to check that out too :)


And the size of the Lua runtime is nothing short of beautiful. I have yet to find a use for Lua though, so I'm still waiting for an opportunity to work on something with it.


I find it's used a lot in places you wouldn't expect. I found it used in a car radio, the modem used in my house, and a few other places. It's lightweight if you want to use it on embedded systems (for example, NodeMCU) if you're into that kind of thing.


Iirc its used on some digital cameras as well.


I'm using it at work to parse SIP messages (with LPeg) for one of our customers (one of the cellphone carriers). Between LPeg for parsing, and coroutines in Lua, the code is pretty easy to read and follow.


I forgot to emphasize that I'm particular about syntax. I amended my comment accordingly. I also don't like the global-by-default bit, but maybe my ideal language could be a syntax layer atop Lua?


Metatables are your friend.

    -- LuaJIT
    
    local oldG = _G
    local newG = setmetatable({}, {
        __index = oldG,
    })
    setmetatable(oldG, {
        __index = function(t, k)
            return rawget(newG, k)
        end,
        __newindex = function()
            error('attempt to assign global variable')
        end,
    })
    _G = newG
If you want to assign a global variable, `x = 5` will error but `_G.x = 5` won't.


If you ignore the class support of moonscript, you can pretty much use it to do what you want. It doesn't have the "global by default" silliness, to start with.


That actually wouldn't be hard to make; just use a custom eslint rules!

If you want implicit returns and really want to disable "fancy features", babel plugins are fairly easy to create – blacklisting certain constructs wouldn't be very difficult, and implicit returns aren't so hard (you can copy my implementation[0] from a JS superset I built called LightScript[1]).

EDIT: bonus of building on babel is that you can get static types for free à la Flow (though typechecking implicit returns may be a pain).

[0] - https://github.com/lightscript/babel-plugin-lightscript/blob...

[1] - http://lightscript.org


Very cool! I can live without implicit returns. Thanks for the info!


Clojure [1] seems to meet all of these criteria. It's a functional language where you can get away with using only a few data types, while also supporting (most of) the rest of the things you mention.

[1] https://clojure.org/


I like the idea, but I want it to be embeddable and since I'll mostly be embedding this in some syntactically C-like language, I would like the scripting syntax to not look so out of place. I updated my previous post to say as much.


Moonscript uses local by default (compiles to Lua). Here is your code in Moonscript:

    foo = {
      x: {1, 2, 3},
      y: (a, b, c) ->
        sum = a + b + c
        sum * sum
    }


Yeah, now just strip away all the other stuff and we'll be good.


Not sure why this was so controversial... I mentioned my requirements in my original post and moonscript clearly doesn't satisfy them. Sorry if that hurts feelings...


I love the syntax that Coffeescript-like languages are converging to, like Moonscript, tj's luna (https://github.com/tj/luna) and Pogoscript (http://pogoscript.org/). They fall way on the right of the "computer code vs. human code" spectrum, which is handy when writing applications as quickly as possible rather than focusing on fine details like implementation and performance. It's a shame I don't see it often in the wild, except for Atom and a few others.


Curious what you might think of LightScript (http://lightscript.org) – would love feedback!


Thankfully, GitHub starting gutting Coffee from Atom a long time ago. There's still too much in there though.


While I understand the move to es6 or whatever we're calling it now, a part of me really misses coffeescript. I found it to be very elegant


I agree - I love coffeescript's syntax. It feels like comparing python to java-esk syntax (I mean without the type checking, etc)


Agreed. Coffeescript was excellent lipstick for the pig that is javascript.


Not particularly a fan, that analogy is solid. To me, a pig in lipstick seems somewhat more disturbing than one without.


I was riffing on an old expression of putting lipstick on a pig.

https://en.wikipedia.org/wiki/Lipstick_on_a_pig


I realize that, but the point of the saying is typically that a pig with lipstick is little or no better than without. My point is that Coffeescript makes JS worse. It just adds an additional layer of complexity when you end up debugging the same old JS traps. The saying still holds up, because a pig wearing lipstick could actually be worse than a plain pig.


Ah, well in that case we are in disagreement. I think Coffeescript is a big improvement. It produces JS code that conforms to The Good Parts™ without having to remember to always add that third equals sign or the var keyword to avoid making a global and other such ridiculous bull-ka-ka.

So you end up not debugging the same old JS traps as much because it is a lot harder to write them. (You get to debug shiny new JS traps instead, but there's only so much a good lipstick can do.)

But it's fine if you prefer javascript to coffeescript. Tastes differ. Some people like bull-ka-ka. runs away


spread some love for moonscript (and its inspiration coffeescript of course, my daily bread-and-butter) - to my mind the best possible syntax for a (dynamic) programming language. python being second place.

is it possible to get a tattoo with significant whitespace?


moonscript powers the itch.io indie marketplace - the original announcement has a quick write-up: http://leafo.net/posts/introducing_itchio.html

After spending years working with Ruby or Java, it was a very nice change of pace. Lapis (leaf's web framework) is crazy good as well: it's easy to write fast code, see this article on coroutines: http://leafo.net/posts/itchio-and-coroutines.html

(disclaimer: I work there with Leaf!)


Had there been any scaling problems for Lapis-powered apps that you have worked on?


Some of the stuff in Moonscript feels a little overdone and magical, like parens-less function calls and the syntactically significant whitespace - but others really feel like they're fixing the gaps in Lua. Moonscript's nice lambda syntax, ternary operator, default local, implicit return, etc.

But I don't think I really needed the ! operator, for example.


fixing the gaps in Lua. Moonscript's [...] default local

Roberto Ierusalimschy, the creator of Lua, believes that "Local by default is wrong." [1].

I don't necessarily agree that local-by-default is inherently wrong, but every implementation of local-by-default that I've seen has had flaws.

It looks like MoonScript is no different - its scoping rules seem to suffer from the same "madness" as CoffeeScript [2].

[1] http://lua-users.org/wiki/LocalByDefault

[2] https://donatstudios.com/CoffeeScript-Madness


Honestly, it seems like implicit variable declaration is a bad idea. Some minimal keyword to indicate "this is a new variable in this scope" seems necessary for sane scoping. It nicely prevents a class of bugs of "oops I made a typo in that assignment and created a new variable" gets caught.


Indeed. Is "let x = y+y" so bad? I wonder how much of the resistance owes to longer words like "local"/"define", or ugly abbrevs like "mut"/"var".


So what would you use for declaring a variable that won't be assigned to until lower scope? Like a variable that will be assigned inside a try/catch block but needs to outlive it so it's declared before the try?

Just

    let x
? That looks weird. Or we're back to "var". Or throwing another keyword at x?

    let x exist
Also weird. I've often dreamed of a nice lua-like math with syntax aimed at being familiar to a first year college math student, and "let" is an obvious keyword for variable declaration, but unless you go pure functional you need to be able to pre-declare variables.


I'd make try/catch an expression, not a statement, like in Lisp -- it'd still be "let x = try {...}...". I agree that 'var' for mutable bindings is a reasonable design. In E it's 'def' for immutable and 'var' for mutable, for instance; and you can also write just 'def x' and then 'bind x := 42' further down (with single-assignment semantics). But though that's nice to have, you can do all right without it; if there was just a single form of definition, with lexical scope, I'd still prefer it to Lua or Python's designs -- clearer and less error-prone and still concise.


I'd use "set" instead of "bind" and be consistent that assignment leverages the vanilla equality operator (since we're already talking about that for "let") but yeah, it sounds like you've been thinking about the same stuff I have.

It sounds like you've been pondering the same problems I have.

In my case it's because I've been working on a rules-engine for scientists and currently we're using SQL for the rule language and some of the syntactic warts of SQL are tripping us up.

I keep meaning to learn enough Racket Scheme to experiment with implementing such a language in macros.


FWIW, Common Lisp's macro system is relatively simple. I've been meaning to check out http://beautifulracket.com/ sometime to learn about the Racket way.

Maybe Datalog could make a cleaner base for your project? Just a random thought; bound to be lots more considerations.

Yeah, I have a project right now too where these questions come up -- https://github.com/darius/squeam -- though it's not really ready for anyone.


You can use 'local' to have sane scoping (shadowing) in moonscript though:

    -- will print:
    -- 15
    -- 0
    -- 15
    -- 10

    y = 0
    goodfun = (x) ->
        local y
        y = 10
        y + x
    
    badfun = (x) ->
        y = 10
        y + x
    
    print(goodfun(5))
    print(y)
    print(badfun(5))
    print(y)


Eugh, I know you aren't defending that CoffeeScript article, but it hugely exaggerates the 'problem' by not acknowledging module scope (see the comments).


I was on a team that used Lua in production on tens of thousands of machines running 24/7. The Lua interpreter was the weak link in the system, requiring at least daily restarts. Squashed so many bugs over the years, and still found more and more all the time. What I'm saying is, the Lua interpreter itself is flawed and I can't imagine using it as a runtime if I had the chance to avoid it. That said, the language was fun and fast and got the job done. The interpreter could use some work.


This page lists every known bug in the Lua interpreter for the last 14 years, along with its fix: https://www.lua.org/bugs.html

The average is 8 bugs per year, most fairly obscure. Every Lua release fixes all known bugs.

Roberto, the chief architect, is kind of fanatical about quality: https://www.youtube.com/watch?v=yU5QNKpATxk

I do not believe the interpreter is as flawed as you claim.


Agreed. In my experience it is extremely solid code. I haven't come across anything that I could imagine leading to a need for daily restarts.

And I speak as someone who reported one of the bugs on their bugs page.


I think Lua interpreter itself is fairly stable. But the problem is that it lacks the proper mechanism for language upgrade or deprecation---for example we have found a (undocumented) bug affecting Lua 5.1.x which has been silently fixed in 5.2, but we were stuck at 5.1 so we had to fork the interpreter. That kind of things. The development process of Lua is definitely flawed in my humble opinion.


Well, this was a few years ago to be fair, and we probably had one of the largest and most intensive Lua deployments in production at that time, if I had to guess. I'm just reporting my experience then, which is just what I experienced. I'm glad to hear that maybe it's better now, and I very much appreciate the hard work and dedication of the developers behind it!


Ever used luaJIT?


No, I think we played around with it but decided to stick with the known quantity of the mainline interpreter, 'devil you know' and all. Because the types of bugs we found were not readily apparent at first, the types of things that would only manifest in statistics accords thousands of machines on 24/7, so pushing a new interpreter out was seen as too risky


I find Lua itself very programmer friendly.


Agreed, but it looks like it adds a layer of syntax to give you things like classes without the user having to leverage metatables.


That is correct. It's also done dynamically so you don't have any external dependencies required when transpiling to Lua code.

EDIT: I am not saying this is better - it could certainly be improved or, as is the case with a project of mine, injected into the runtime as a global value. It's just good for running MoonScript alongside Lua.


Except that arrays start on 1, right?


Exactly. It's more intuitive and you have less `x-1`'s in your code.


Arrays starting at 1 and being closed intervals is mostly just worse than starting at 0 and being half-open intervals.

Here's an expression to take an integer b and "mod it" into an interval from a to c, given the normal convention from C++ or Python or whatever where intervals are [a, c):

  (b-a)%(c-a)+a
And here it is in the lua convention where intervals are [a,c]:

  (b-a)%(c-a+1)+a
This is a generalization of what you'd do if you got a really big random integer and you wanted to use it to pick from an array. For that, normally you'd write

  n % array.length
but in Lua you should be careful to write

  (n % #array) + 1
You can read some other opinion about this here https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...


Yes there are many algorithms that are naturally 0 indexed. But there are also many that are naturally 1 indexed! This is somewhat unscientific, but I once went through the first quarter of an algorithms textbook. And counted how many were naturally one based or zero based. And on most it didn't matter, but there were slightly more one based ones.


Can you give us some examples of one-based algorithms?

I've done a lot of programming in one-based languages, and now avoid such languages as much as possible (sometimes have no choice) because nothing seems to naturally fit into that.


This is unlikely something you'll use in practice, but I just read about it so it came to mind.

A Braun tree is a binary tree that represents a flexible array (indexable everywhere and extendable at both ends). The gist of it is that you take your array index and read it as a binary string that tells you how to go down the tree. If your index is 1, then you're at the right place and you stop. Then you go left/right depending on whether the remainder is 0/1. That is the 1-based interpretation, and it works because 1 is in the first digit while 2 and 3 need the next digit as well.

If you want to use 0-based indices, it doesn't work out as cleanly, because 0 and 1 share the same digit, while 1 and 2 use different digits. So now you have to branch on whether your index is odd or even, and then either (i-1)/2, or (i/2)-1.

It's not a big deal either way, but it turns a simple to visualize data structure into one where you have to think about the indices.


Binary heaps are more commonly known and nicer to work with in 1-based arrays for the same reason. If arrays are 1-based, a binary heap's node's children are at 2n and 2n+1, and the node's parent is at n/2.

You shouldn't need a branch in a 0-based Braun tree though. (n-1)/2 should be correct all the time.


any kind of numerical method (RKs, FDs, CN) where the n-th step a_n = f(a_(n-1)) and hence you define NUM_STEPS and the the result is a[NUM_STEPS] where a is your numerical lattice.

you could work in a 0-based index and your n-th step is a_(n-1) and so the final step is a[NUM_STEPS - 1]

in numerical analysis I don't believe i've ever cared that much about slicing. if i wanted to look at a single element in a matrix a_(ij), then I call the matrix a[i,j]. if you had to implement a pivoting algorithm then it's quite useful to call the pivot element a[i,j] and pivot row a[i,:]. i believe most devs would prefer not to have to call elements everytime a[i-1,j-1].

guessing most math guys have worked with both 0 and 1 systems and honestly don't care that much.


I don't really get your reasoning.

For an iterative method, you have an initial value a[0], then take your NUM_STEPS steps, so the final result is a[NUM_STEPS]. If it's one-based, the final result is a[NUM_STEPS+1]. This type of thing trips up my Matlab (one-based) students all the time.

In zero-based linear algebra, the indices i and j start at 0, so the top left entry of a matrix is a[0, 0], the pivot element is a[i, j] and the pivot row is a[i, :]. There isn't any adjustment needed.

Finite differences, Simpson's Rule, etc., seem more natural with zero-based too. x_0 is the left-most value, x_n is the right-most value, and the interval is divided into n pieces.


yeah i think maybe numericals aren't the best example since the initial value is always labeled x_0 and if you want to create a 2d array with the first array your X_0s then creating an N + 1 array makes sense.

your matrix example is totally bizarre though. the top left of a matrix A is always A_(1,1). so not sure what a[0,0] means. a 1x1 matrix is always 1 element, which is a[1,1]. the number of elements in a mxn array is m*n.


> the top left of a matrix A is always A_(1,1)

In maths notation it usually is, yes, but in a zero-based language it's usually a[0, 0] (and I've seen maths and CS papers where it's 0, too). The number of elements in a mxn array is still m*n, but the indices don't go that high. For example, in Python you'd loop through m rows with "for i in range(m)", which will go from 0 to m-1 (actually, you might do "for row in a" and not bother with indices). You hardly ever need to do m-1 manually - if you want the last row it's just a[-1, :].

I'm not saying zero-based is always better, but I haven't seen many situations where one-based is preferable.


As Jef Raskin famously observed: "intuitive" just means "familiar". O- vs 1- based indexing is a source of curious passion for many people, as it really just reflects what you first learned and (consequently) what mental model you are using. Like most matters of taste, there is no objectively correct answer.

If you came up in assembly language and C, like me, you tend to think of array indices as offsets, and the first item obviously has an offset of 0. Anything else is absurd. If you came up in any number of environments that view collections like arrays from the standpoint of something like set theory, then the first (1st!) item clearly has an index of 1. What would a zeroeth item even mean?!. See---it's just point of view and familiarity.

I resisted Python for years because I just couldn't get past the clearly fragility and cognitive burden of something so mind-bogglingly stupid as meaningful whitespace. It took me a while to realize this was much more about me than about Python.

You bounce off of these contextual things all the time in Mathematics: Is 1 prime? Do the Whole numbers include 0? Etc., etc. The answer is---depends on who you ask and what you're working on. It can be implicit, especially to a community (like, say, users of a programming language), or it may need to be explicit to avoid confusion "In this house, we obey the Fundamental Theorem of Arithmetic! 1 is not prime!" As long as you get those particular ducks in a row, the problem is mostly just impedance from people mistaking their personal experience for natural law.


I think the exact opposite. These things are always very subjective.


Adding superlatives to your stuff is, to me, just noise. e.g: "fast", "simple", "friendly", "lightweight".

I would rather provide conclusive proof, like some side to side comparison of features to illustrate how idiomatic MoonScript is supposedly friendlier than Lua.

Personally I think the changes are not necessarily in the right direction:

For example, what if you have a typo in a variable identifier when assigning a value to a variable? Now you have a new variable. Where to look for the definition of a variable? It depends on what runs first now. That's not friendlier. CoffeeScript made the same mistake in the name of simplicity and it quite frankly doesn't add any value. The time you saved typing "local" will be now consumed several times debugging and code reviewing... and you will have to pay more attention during your code reviews.

Then, removing braces and other delimiters. That's not necessarily better either. Let's remove lane delimiters from streets, traffic signals, stop and yield signs, and let's make it all implicit based on rules you can follow in your head. See? doesn't work.


> I would rather provide conclusive proof, like some side to side comparison of features to illustrate how idiomatic MoonScript is supposedly friendlier than Lua.

In the reference manual, it is quite literally a side by side comparison. MoonScript code is shown to the left, with Lua to the right. MoonScript provides shortened code with minimal overhead compared to how it would be implemented in Lua, and you can see how by viewing the reference manual.

> For example, what if you have a typo in a variable identifier when assigning a value to a variable? Now you have a new variable. Where to look for the definition of a variable? It depends on what runs first now. That's not friendlier. CoffeeScript made the same mistake in the name of simplicity and it quite frankly doesn't add any value. The time you saved typing "local" will be now consumed several times debugging and code reviewing... and you will have to pay more attention during your code reviews.

This is the same problem with Lua. The best solution that I've found is to have an editor that highlights variables in different colors; once the color has changed (and it should based on if it's similarly named to another variable, so that `asdf` should be a very different color to `asdg`) you know it's a different variable.

> Then, removing braces and other delimiters. That's not necessarily better either. Let's remove lane delimiters from streets, traffic signals, stop and yield signs, and let's make it all implicit based on rules you can follow in your head. See? doesn't work.

Except you can still know when a block ends. Your comparison isn't really fair because it's just a cosmetic change. It's as though stop signs were a square instead of an octogon, not if they were just completely removed.


The unique color solution doesn't scale: Imagine that across multiple files. Imagine that in a large project. Imagine that with color blind people.

Then, when you remove parenthesis, it's the same as removing a lane delimiter. Matching parenthesis can already be confusing, now try matching invisible parenthesis. That creates more problems than it solves.

If this author wants to borrow aspects of Python, I suggest reading PEP 20: The Zen of Python. "Explicit is better than implicit".

Implicit parenthesis, implicit braces, implicit returns... the more implicit stuff the more work you need to do in your head, and the more you rely on humans. I thought programming was about giving more work to machines, not to humans.


> Then, when you remove parenthesis, it's the same as removing a lane delimiter.

As someone that vastly prefers parenthesis, no, it's not. The block is just bounded by syntactic structure, not by delimiter. It's like replacing lane delimiters with rippled road texture. The delimiter is different and in my opinion inferior, but it's not gone, it's just replaced by some other indicator.


Except that that whitespace is not used only for that purpose, it could mean also something else, right? We trade an unambiguous set of characters --the parentheses-- by whitespace, and make it context sensitive. How is that now more readable? As someone who has had to match these whitespace based parenthesis I can tell you it's not more readable.

It's more like someone determined that parenthesis were not visually palatable and decided to remove them. Like "hey, that fire extinguisher doesn't look very elegant, let's remove it, or wrap it in wallpaper so nobody can see it".


> Except that that whitespace is not used only for that purpose, it could mean also something else, right

It's not just whitespace, it's the specific formatting of whitespace. It's just a character, like any other, only defined by it's lack of distinguishing characters. Instead of whitespace, block continuations could be defined by pipes, or angle brackets, or any number of things. They chose wihtespace.

> We trade an unambiguous set of characters --the parentheses-- by whitespace, and make it context sensitive.

Parenthesis are often context sensitive in languages. It's very common for them to handle both code block and object/dict/hash definition. Languages often use '+' for both numeric addition and string concatenation (which in my eyes is often more problematic and less justified than whitespace as a block defining element).

> How is that now more readable? As someone who has had to match these whitespace based parenthesis I can tell you it's not more readable.

"Readable" is subjective. Unless you have experience with it, you would probably find APL more than unreadable, likely impenetrable. Experience can vastly change what one find easy to understand, and easier to learn doesn't necessarily mean better in all respects.

> It's more like someone determined that parenthesis were not visually palatable and decided to remove them.

Or, it actually is that some people prefer it, even if you and I do not, and that's what it is, a preference implemented in the languages they decided to make. It is, to me, subjectively worse, but the key word there is subjectively. There are trade-offs between the benefits and detriments of that style, but we shouldn't fall into the trap of assuming we can make objective claims as to the quality of the choices of languages that prefer structured whitespace as a syntactic element when it's obvious a large percentage of people find it beneficial.

In the end, unless you're being forced to use this language, I'm not sure why it matters what choice they made with regard to this. If you don't like the choices made by this language, don't use it, and your actions will speak for themselves. If enough people feel that way, the language will die out or change. If enough people do like it how it is, it will continue, as it should, because there's no reason we should restrict other people's choices when it comes down to what is by large an aesthetic choice.


> Languages often use '+' for both numeric addition and string concatenation

Perl uses . or ~ depending on which version you are using. It also uses braces for code blocks and postfixed ifs which prevents errors like:

  if (x)
    y
  z
Instead of:

  if (x)
    y
    z
It prides itself to being close to natural language yet still uses braces.


I'm aware, and Perl is specifically what I had in mind when I noted that. It's a little different of a case in Perl than in a language that is more strongly typed, like Python, as Perl uses operators to cast into the appropriate types for the operation and Python makes it an explicit action (which is why Perl has separate operators for most string and numeric operations, such as < and lt, > and gt, == and eq, etc). A Python programmer without much experience in Perl might think this is a case of implicit action, but it's actually rather explicit, as the string and numeric specific operators explicitly define exactly what to do with the two operands.

It's a pet peeve of mine that languages have promoted + for addition and string concatenation when concatenation and addition are distinct concepts that don't really share a lot in common beyond the surface. To me, in new languages, it signals that someone likely hasn't thought through that issue very clearly, or has and just doesn't care about consistency (which I think it important in a language). Python at least has the excuse of age (even if it's not all that old compared to many others).


> It's not just whitespace, it's the specific formatting of whitespace.

Yes. Specific, context sensitive, that requires disambiguation in your head and therefore harder to read.

> We trade an unambiguous set of characters --the parentheses-- by whitespace, and make it context sensitive. Parenthesis are often context sensitive in languages.

Yes, but they're consistent with the mathematical notation everyone has used over their entire life. By the time you pick up a programming language you have most likely used parenthesis for a while.

I am going to create a new language 99% identical to English, where fork means knife, knife means spoon and spoon means fork. Have a happy time translating paragraphs of text involving those 3 objects from English into my language.

> "Readable" is subjective.

Yes. But implicit vs explicit is not subjective. Implicit meaning takes non-zero effort to understand. It works when you the implicit subject is not relevant and you can be abstracted from it, but in this case there is no encapsulation taking place. Therefore, we could objectively say it's less readable. Quod erat demonstrandum.

> Or, it actually is that some people prefer it, even if you and I do not, and that's what it is

Now you have 2 ways of doing the same thing with no added value. Now you have more possible ways of writing down an expression, if you are parsing an expression or refactoring your code with a regex now it's much more complex. If you are writing a coding standard now you will have to make rules for it, and people may not reach agreement on those rules. People may have meeting to discuss which one to use and why, and spend time moving from one style to the other. It's a waste of time.

In 2017 there's still people fighting over tabs vs spaces. Now we add another dimension to it: spaces vs parenthesis. An endless source of bikeshedding.

> In the end, unless you're being forced to use this language

That's not how it works. I have had to use languages that I dislike because that was what was needed in my organization.


> Yes, but they're consistent with the mathematical notation everyone has used over their entire life. By the time you pick up a programming language you have most likely used parenthesis for a while.

As opposed to the natural visual distinctions that we make all the time? Braces are a learned concept. Having something visually offset might be a learned concept, but if it is I bet it's so early it's prior speech developing. People notice visual patterns. An offset bit of text forms a visual pattern. Again, I don't prefer it, but I think you're reaching in your arguments against it.

> I am going to create a new language 99% identical to English, where fork means knife, knife means spoon and spoon means fork. Have a happy time translating paragraphs of text involving those 3 objects from English into my language.

So, exactly the same situation we have with English today, where it changes and slang is used? I'll have to ponder that problem as I kick it in my crib tonight. Or maybe I'll just settle in and watch the boob tube. Surely replacing some words with others isn't and intractable problem?

> Implicit meaning takes non-zero effort to understand.

Whitespace blocks are not always implicit. They may be well defined within the language they are used in (such as in Python). It's no more implicit than < means less than. Both are defined within the language, and < happens to also share meaning with mathematical notation, which makes it easier to grasp initially for those familiar with that, but there's nothing implicit about either as they both use the inclusion of one or more symbols to confer meaning (even if they are space characters), rather than the exclusion of information. You are confusing whitespace for no-space.

> Now you have 2 ways of doing the same thing with no added value.

Of course there is added value. People prefer it. People's preferences have value.

> Now you have more possible ways of writing down an expression, if you are parsing an expression or refactoring your code with a regex now it's much more complex.

That depends on the language and whether there are multiple ways of defining a block. Unless you expect your expression to parse C, Lisp, Python, Smalltalk and Prolog correctly it it's just those pesky Python whitespace blocks that are holding you back. Within Python, space indented blocks are canonical. If you think that is causing too many ways to define how to perform something in different languages, you're probably better off not looking at some of the other languages I listed earlier...

> In 2017 there's still people fighting over tabs vs spaces. Now we add another dimension to it: spaces vs parenthesis. An endless source of bikeshedding.

Not really. Tabs vs spaces is something people that interact in code with each other have to deal with. I doubt Perl and Python developers get upset when they load each other's code, in the other's language, and see the other used spaces instead of parenthesis[1] or vice-versa, because that choice doesn't make sense within those languages. Sure, try to use indentation to define blocks in Perl. It won't do you any good without the parenthesis, because Perl doesn't support that choice.

1: It's obvious at this point we're actually talking about curly-braces, and not parenthesis, right? I'll keep using parenthesis for now for consistency's sake.


Parentheses have been used for evaluating functions and establishing operator precedence for literally hundreds of years. It's taught in every school in every country and most books will contain this notation.

But since everything is relative, it doesn't matter anymore. Let's delimit function evaluations and operator precedence with the words "cat" and "dog" from now on. Since everything is a preference, and is relative, it doesn't matter.

However, what you fail to see is that in reality a choice like this involves effort, it has an economic cost, it's not only preference.


How are they just noise? If somebody says that something is "fast", it's because it's purpose is to be fast and nothing else. If something says it's lightweight it means it's aim is a minimal size. They are not noise, they are mission statements.


Saying "our goal is to be x" is different than "we are x".

One is an aspiration, the other one is a confident claim.


Many languages allow for assignment and instantiation on the same statement... I deal with this all the time in ruby and my editor simply highlights vars which aren't used later (quickly leading me to figure out if I had a typo)

As for your traffic example: in many circumstances removing lines and signs makes the roads safer (as reported by the guardian). People pay more attention because of increased uncertainty, and aren't driving (or coding) on autopilot. https://www.theguardian.com/commentisfree/2016/feb/04/remova...

Paying attention is precisely what we're paid to do.


Side by side: the examples on the landing page have a "Show LUA" button, so you can easily compare.


I see it as a toy language. The only person I think would find it useful is leafo himself since he can edit it however he wants. But everyone else should stay away from it. Plain Lua is so much better.


Can't agree with that. Having used both Lua and Moonscript quite a bit I think Moonscript is what most dynamic scripting languages should be. Simple, succinct and does what you need for a scripting language.

I really enjoy using it.


Care to give an example?


The list comprehensions are really great. Most of the time when I'm writing Lua I can't be bothered to write exactly what Moonscript outputs for a list comprehension because it's noisy, but it's what I really would write if I cared about tiny amounts of performance.


Idk. Still not sold. You can do list comprehensions in regular Lua with something like this:

    function listcomp(t, f)
        local r = {}
        for k,v in pairs(t) do
            r[k] = v
        end
        return r
    end
And then, your Lua code would be like:

    listcomp({1, 2, 3, 4}, function(k, v)
        return v*2
    end)
Which would be the equivalent to this in MoonScript:

    [v*2 for k,v in pairs({1, 2, 3 ,4})]
I dunno. Just seems like a wash to me. And then you think about the extra compile step to run MoonScript, and the potential performance hits, and I'm just turned off completely from it.


There are no potential performance hits. In particular the code generated for list comprehensions would be faster than your example because there is no extra function call per item.


Over here it generated

  local b
  do
    local _accum_0 = { }
    local _len_0 = 1
    for k, v in pairs({
      1,
      2,
      3,
      4
    }) do
      _accum_0[_len_0] = v * 2
      _len_0 = _len_0 + 1
    end
    b = _accum_0
  end
It did a pretty good job. Unlike your code, it doesn't have any function calls, and unlike the code I would probably write, it doesn't use the # operator.


It's a general purpose language, you never know who will use it and how.


There are a lot of features that make Moonscript much more convenient than plain Lua. Examples (try the first three in Lua):

- default values for function parameters

- list comprehensions

    [x * 2 for x in *{1, 2, 3}]
- easier iteration (e.g. `for key, value in pairs obj`)

- local is default, not global

- a simple class system if you need it

- `export`, `from` and `import` keywords

- `+=`, `/=`, `*=` etc. operators

- indentation based scope (ok, this is debatable)

I can't think of a single place where plain Lua is actually better. OK maybe `:` is better as a method call operator, but that's it.


> default values for function parameters

    function f(a, b, c, d)
        a = a or 1
        b = b or 2
        c = c or 3
        d = d or 4
    end
> list comprehensions

https://news.ycombinator.com/item?id=14444215

> local is default, not global

I think this is a con.

> a simple class system if you need it

I also think this is a con, because it limits flexibility.

Here's my class system:

    Object = {}
    function Object:new()
        local self = setmetatable({}, {__index = self})
        return self
    end
And how to use it:

    local super = Object
    Shape = Object.new(super)
    function Shape:new(color)
        local self = super.new(self)
        self.color = color
        return self
    end

    local super = Shape
    RedShape = Object.new(super)
    function RedShape:new()
        local self = super.new(self, 0xff0000)
        return self
    end
If I want to add multiple inheritance, or interfaces, it is easy. Because it is just metatables. With MoonScript this is not possible.

> `export`, `from` and `import` keywords

Sounds like a con to me. `require` is simple.

> `+=`, `/=`, `*=`

I agree with this one.


>

    function f(a, b, c, d)
        a = a or 1
        b = b or 2
        c = c or 3
        d = d or 4
    end
Compare with:

    f = (a=1, b=2, c=3, d=4) -> ...
Also note the latter is correct when you call it as f(false).


"Moonscript is like CoffeeScript for Lua"

That's a very succinct description and from skimming the examples, quite apt.

But what I want is Typescript for Lua, specifically to use with Redis. That way I can define interfaces for my keys/values and have static checks on their usage.



Typed Lua might be worth checking out: https://github.com/andremm/typedlua (disclaimer: I haven't used it yet)


Another Moonscript project is https://howl.io - an editor written almost entirely in Moonscript that runs on LuaJIT.


How does MoonScript compare to Wren? https://github.com/munificent/wren


System.print("Hello world")

It kind a looks like someone wanted to make actual Java-script. To me these are way too different to even compare, unless you are talking about performance, but then you are just comparing Lua with wren and there seems to already be some benchmarking of these on wren.io


Can this be used instead of Lua with Redis? Is there an example somewhere of that?


Here's an example of a job runner (≈ cron) in Redis and MoonScript: https://github.com/debrouwere/jobs/tree/master/src. I don't maintain it anymore, but it should give you an idea of how MoonScript can make Redis scripting a whole lot less tedious.



Pretty sure he means redis scripting https://redis.io/commands/eval


Yes, that's what I meant. Thanks!


Yup, just pre-compile the code and load it into redis as normal.


Nice!

We need more programming languages with sane syntax.


I wonder if this would work with NodeMCU development for esp8266 boards.


Lua is a great language and MoonScript improves on it even further. I used MoonScript to write a load balancer with OpenResty recently, and World of Warcraft interface code six years ago. :)




Registration is open for Startup School 2019. Classes start July 22nd.

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

Search: