gkz / LiveScript

LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.
http://livescript.net
MIT License
2.31k stars 155 forks source link

Error thrown from parsing a partial application of a method application chain #746

Open dead-claudia opened 9 years ago

dead-claudia commented 9 years ago

As in, the following throws on both the latest NPM version and the website:

(a.b _ .c)
(a.b _ .c!)
(a.b _ .c d)

Here's the stack trace for each from lsc (same output):

TypeError: Cannot read property 'length' of undefined
    at Chain.exports.Chain.prototype.add (/home/isiah/.npm/.global/lib/node_modules/livescript/lib/ast.js:950:110)
    at Object.anonymous (/home/isiah/.npm/.global/lib/node_modules/livescript/lib/parser.js:96:41)
    at Object.parse (/home/isiah/.npm/.global/lib/node_modules/livescript/lib/parser.js:840:36)
    at Object.exports.compile (/home/isiah/.npm/.global/lib/node_modules/livescript/lib/index.js:45:20)
    at compileScript (/home/isiah/.npm/.global/lib/node_modules/livescript/lib/command.js:197:31)
    at /home/isiah/.npm/.global/lib/node_modules/livescript/lib/command.js:97:9
    at Object.<anonymous> (/home/isiah/.npm/.global/lib/node_modules/livescript/bin/lsc:7:26)
    at Module._compile (module.js:430:26)
    at Object.Module._extensions..js (module.js:448:10)
    at Module.load (module.js:355:32)

By the looks of it, there's no parsing support for this.

dead-claudia commented 8 years ago

@vendethiel Any ideas? Still failing.

rhendric commented 8 years ago

So, the crash is easily fixed—all it takes is changing one . to a ?. Unfortunately, while this now lets those expressions compile, they don't have the semantics that I presume you want—the docs make no mention of the meaningfulness of parentheses when compiling partial applications, and so the compiler dutifully treats (a.b _ .c) as equivalent to (a.b _).c, the same way that (a.b x .c) would be equivalent to (a.b x).c. I see from your tests that you were expecting something closer to the way Scala handles underscore placeholders, where the nearest bounding parentheses help determine the extent of the anonymous function created.

If that's indeed what you're proposing, you should probably flesh out the feature request a little more. One potential niggle is what gets captured at the time of partial function creation; right now, everything does:

a = b: (x, y) -> x + 2*y
c = 10
f = a.b _, c
a.b = (x, y) -> 2*x + y
c = 1000
f 1 #=> 21

Contrast with the similar but non-capturing version:

a = b: (x, y) -> x + 2*y
c = 10
f = -> a.b it, c
a.b = (x, y) -> 2*x + y
c = 1000
f 1 #=> 1002

In an expression like (a.b _ .c d .e f), in your proposal, does a.b still get captured? Do d and f? Or do you abandon the capturing semantics to make (any expression including _ as an argument somewhere but not inside any deeper parentheses) semantically equivalent to (-> that same expression with _ replaced byit)?

dead-claudia commented 8 years ago

First, I thought I'd clarify with my current stance. In my opinion, it would be best rejected, since it's not just a simple method call, function call, or property access. Otherwise, it's nearly impossible to infer reliably. By that point, you probably should already be using a normal function, since even if it's trivial to the programmer, it's no longer trivial to the compiler. To my knowledge, even Haskell's GHC would reject that kind of thing.

The reason I didn't was because I was uncertain at the time, and hadn't yet put much thought into it. I hadn't really thought about which was the better route.

I'll have to convert the tests, but I'm not sure exactly what the message should be in the rejection.

On Fri, Feb 19, 2016, 00:43 Ryan Hendrickson notifications@github.com wrote:

So, the crash is easily fixed—all it takes is changing one . to a ?. Unfortunately, while this now lets those expressions compile, they don't have the semantics that I presume you want—the docs make no mention of the meaningfulness of parentheses when compiling partial applications, and so the compiler dutifully treats (a.b .c) as equivalent to (a.b ).c, the same way that (a.b x .c) would be equivalent to (a.b x).c. I see from your tests that you were expecting something closer to the way Scala handles underscore placeholders, where the nearest bounding parentheses help determine the extent of the anonymous function created.

If that's indeed what you're proposing, you should probably flesh out the feature request a little more. One potential niggle is what gets captured at the time of partial function creation; right now, everything does:

a = b: (x, y) -> x + 2yc = 10f = a.b , ca.b = (x, y) -> 2_x + yc = 1000 f 1 #=> 21

Contrast with the similar but non-capturing version:

a = b: (x, y) -> x + 2_yc = 10f = -> a.b it, ca.b = (x, y) -> 2_x + yc = 1000 f 1 #=> 1002

In an expression like (a.b .c d .e f), in your proposal, does a.b still get captured? Do d and f? Or do you abandon the capturing semantics to make (any expression including as an argument somewhere but not inside any deeper parentheses) semantically equivalent to (-> that same expression with _ replaced by it)?

— Reply to this email directly or view it on GitHub https://github.com/gkz/LiveScript/issues/746#issuecomment-186065533.

vendethiel commented 6 years ago

So, first: https://github.com/taku0/placeholder_syntax_for_coffeescript/blob/master/src/placeholder.coffee https://github.com/taku0/placeholder_syntax_for_coffeescript#scopes-of-placeholders spells out the Scala-like rules (they're not quite like Scala, apparently), but they're fairly complex. Also, this was discussed (a bit) in #19. It doesn't actually support a(_).b, though, so...:

In an expression like (a.b _ .c d .e f), in your proposal, does a.b still get captured?

That's basically the difference in LS between partial application and currying (f _ does capture, (+ f) doesn't). I seem to misremenbering that we transformed f _ to -> f it in some cases... It doesn't really matter to me either way. I'd say capture a.b but not f and d ("where the execution stops"), but I don't mind either.

Or do you abandon the capturing semantics to make (any expression including as an argument somewhere but not inside any deeper parentheses) semantically equivalent to (-> that same expression with replaced byit)?

I'd like that, but I think making a.b _ and a.b _ .c too different would be a mistake.