leafo / moonscript

:crescent_moon: A language that compiles to Lua
https://moonscript.org
3.2k stars 192 forks source link

Patch ellipses quirk in lua #83

Open exists-forall opened 11 years ago

exists-forall commented 11 years ago

In lua, for some reason (possibly to optimize the internal layout of the stack and upvalues?), the pseudo-variable ... is not lexically scoped like other variables.

This is an error:

wrap_args = (...)->
  -> ...

But this works:

wrap_args = (...)->
  args = {...}
  -> unpack args

I see no reason why moonscript couldn't patch this problem with lua at compile-time, detecting when ... is being lexically inherited and add args = {...} and unpack args behind the scenes.

With this in place the first example should compile to this (under the old internal variable naming convention, that is)

local wrap_args = function(...)
  local _vararg_0 = {...}
  return function()
    return unpack(_vararg_0)
  end
end
stevedonovan commented 11 years ago

Cool, but it will be not as efficient and people need to be aware of that.

exists-forall commented 11 years ago

When writing code like this, there isn't any possible alternative than packaging it up into a table. So, when you say it's "not as efficient", that gets me to wondering "not as efficient as what?" There's no code that could accomplish the given task any other way, so Moonscript isn't adding any overhead. It's as fast as it could possibly be.

I agree that efficiency is always a good thing to keep in mind, but I seems a little counterproductive to worry about inefficiencies of lua. There are things that could be sped up in the moonscript compiler, such as the (function() foo end)() construct that's used in certain corner cases like when a do block is an expression. This, however, is not something where there is any room for improvement on moonscript's part, and I don't we should be sweating it too much :)

Moreover, it's not actually all that slow. For most of lua's history (5.0 and below I think), having ... in the argument list didn't give you a ... pseudo-variable, it gave you an args table and an argcount (or possibly argc) number as implicit parameters, which is more or less what we're doing here manually. Lua has had its reputation as one of the fastest scripting languages for a very long time, and I don't think they would've added that initial version of varargs if it was a glaring speed issue.

stevedonovan commented 11 years ago

Fair enough! I'm micro-optimizing again. Was pleased when the arg abomination ended, although 5.1 still keeps it (LuaJIT properly disapproves). The question would be rather: how often do we need to do this in reality? If it's a few cases, then explicit isn't bad.

exists-forall commented 11 years ago

I agree it's not too common a problem for a lot of people, but I use nested functions at every opportunity cause they're just so flexible and useful, and because moonscript provides such a clean syntax for them. When you're programming in this sort of semi-functional style (possibly more macro-like than functional, actually, since although they're often grouped together, higher-order-functions and immutable data don't have too much to do with each other), you often forget that you're even creating anonymous functions - you just feel like you're using ordinary language constructs. This means that errors about ... can be quite disconcerting and counterintuitive.

I've experienced it enough times that it seems like something in need of attention, but for other people that may not be the case. Still, I think higher-order functions and mild meta-programming should be encouraged, and that the current state of affairs regarding ... is somewhat of a deterrent to that philosophy.

I actually ran into this problem when trying to write a permutations iterator, which can be found on the wiki.

import wrap, yield from coroutine

export permutations = (first_range, ...)->
    first_range = {1, first_range} if 'number' == type first_range
    if ...
        wrap ->
            for i = first_range[1], first_range[2]
                for sub_index in permutations ...
                    yield {i, unpack sub_index}
    else
        wrap ->
            for i = first_range[1], first_range[2]
                yield {i}

Do you see the problem? Neither did I. Until it wouldn't compile, and I had to kludge a rest_ranges = {...} variable in. It'd be really nice if moonscript did this for me, at least until lua patches it officially (which I think I've heard some rumors of on the mailing lists, but I could be wrong about that).

stevedonovan commented 11 years ago

OK, I'm convinced - that is a tricky error to see. I've also found myself using higher-order functions more in MS because they are less painful to type (I've always been pushing for a 'short lambda' form in Lua itself, but there is surprising resistance. Maybe we just like our code compact and readable). BTW, I imagine MS will be an excellent fit to luvit, which is a node.js-in-luajit clone.

exists-forall commented 11 years ago

Higher order functions are so much more pleasant in moonscript than lua it's ridiculous! Sometimes, I'm worried that I use them even when they're not necessary just because I love typing -> and thinking how slick it is :) The only other language I've seen (not counting coffeescript) whose support for anonymous functions is even close to that of moonscript is Ruby with blocks. However, Ruby has the whole proc, block, bound_method, unbound_method, lambda, &arg mess, and there's also the issue of worrying about what the evaluation context is (IE is the block going to be passed to instance_eval, class_eval, or left alone). Moonscript just has plain-old, elegant, predictable functions, which can do everything ruby can do with way fewer issues to worry about. Moreover, Moonscript provides an easy way to pass multiple anonymous functions to a function, for implementing constructs like:

try
  main: ->
    -- possibly dangerous code
  catch: (e)->
    -- handle exception
  finally: ->
    -- always run

In ruby, doing anything similar requires a lot of voodoo.

I hadn't heard about luvit, but I'll check it out right now. Sound very interesting.

When listing all the things moonscript would be a good fit for, like luvit, love2d, wxWidgets, etc..., I think it would be a big time saver just to say that, in general, moonscript is a good fit for any place where lua is currently used!

The only exception to that rule, in my opinion, is teaching programming to beginners. Moonscript's syntax has a few too many ambiguities and shortcuts for somebody struggling to understand the difference between a function call, assigment statement, looping construct, etc. Every feature moonscript adds over and above lua's feature set is to save typing, which is fantastic for a lua veteran who values productivity and compactness, but is probably confusing for a novice. When you're new, the more explicit everything is, the better. Features like the with statement, instance variable assignment in the argument list, :x as an alias for x:x, etc. all seem like they'd just be stumbling blocks until you're totally comfortable with their longer equivalents.

stevedonovan commented 11 years ago

I would totally agree about beginners, but they're not the target. I spent some time with Haskell last year, to understand the fuss, and got as far as the 'So that is what a monad is!' moment. But I didn't have anything non-artificial I could do with it. MS has the same clean syntax, without the sometimes overbearing purity obsession. (I do think people confuse programming with religion too often). Plus, I know the Lua ecosystem pretty well, having written parts of it.

The try/catch example is rather cool - could you add it to the 'exception handling' page?

Hm, so perhaps we really do need a mailing list ....

exists-forall commented 11 years ago

<rant type='incoherent'> Haskell does feel quite a bit like an academic toy. I think that functional techniques are great, and are actually one of the most valuable general purpose programming tricks I've learned in a while, but that writing everything functionally is completely impractical.

The idea that "oh, you don't need loops. Everything can be written in terms of recursion, and if you're using loops you're probably not writing code that's mathematical enough" sounds about as reasonable a restriction to put on someone as "oh, you don't need comments. They don't add any semantics to your code, and anyway, if it's not obvious what your code does without any explanation its probably not good code." I have nothing against self-explanatory code, or recursion, but it's completely untenable to program within an environment that enforces dogmatic rules like that. You need something that encompasses multiple paradigms.

I find that Lua is a very nice little procedural language, but that it's a little impractical because procedural programs quickly become unwieldy. There are glimmers of OO and functional programming here and there, but you always feel like you're swimming upstream a little bit when you try to use them in bulk. Luckily, in Lua, unlike many other languages (C, Perl, etc., where the languages are designed in such a way that they cannot be fixed, and any attempt to fix them ends in disaster (C++)), this is not a shortcoming of the underlying semantics, but rather just a slightly verbose syntax. When you bring Moonscript into the picture, the semantics shine through completely, and you can use prcoedural, OO, and functional paradigms in complete harmony. This is the most productive situation, IMHO, because the more tools you have at your disposal the greater the capacity for abstraction and expressiveness.

Instead of a mailing list, maybe github should just rename "issues" to "discussion". I find that often when it feels like you're misusing something, you're really just using a flexible tool with an overly specific name or description.

For example, Love2D is perfectly adequate for 2D simulations or demonstrations, or even certain kinds of gui projects (especially gui mockups), but because it's listed as a game engine, there's always that worry that you're trying to fit a square peg into a round hole. It's even good for 3D, if you're willing to roll your own matrices (which reminds me, I should post my moonscript matrix and vector code on the wiki), but it feels wrong doing 3D in an engine with 2D in its name.

Having "issues" renamed to "discussion", while it'll almost certainly never happen, would make the way that the moonscript issues have organically morphed into a mailing list feel like a lot less of a kludge.

I guess my point is, don't let a name or description throw you too much. Focus on what a tool can actually do, not what it's supposed to do, and you'll get a lot more out of your tools. </rant>