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.32k stars 155 forks source link

Inline some immediately-invoked function expressions #1052

Open danielo515 opened 6 years ago

danielo515 commented 6 years ago

Hello, It would be awesome if you could use the _ placeholder for putting a value into an array on a pipeline. Here is an example from a real world babel plugin:

function transformFunction t, functionType, id, {node: {params, body, generator, async}}: path
    path.replaceWith 
    <| expandParams t, (tail params), body, async
    |> t.returnStatement 
    |> -> t.blockStatement [it]
    |> functionType id, (head params), _, generator, async

It is very close to be as beautiful as it could be. This pipe works well, and I'm just being picky of course, but, why the following will not work ?

function transformFunction t, functionType, id, {node: {params, body, generator, async}}: path
    path.replaceWith 
    <| expandParams t, (tail params), body, async
    |> t.returnStatement 
    |> -> t.blockStatement [_]
    |> functionType id, (head params), _, generator, async

I tried all kind of combinations [ _ ], |> [ _ ], but none of them work. Of course I can use Array.of _, but then I think it will be not much difference from my current solution.

Regards

danielo515 commented 6 years ago

A solution that does not introduce any runtime overhead (or at least, any intermediate function) would be this:

function transformFunction t, functionType, id, {node: {params, body, generator, async}}: path
    path.replaceWith 
    <| expandParams t, (tail params), body, async
    |> t.returnStatement
    |> Array.of
    |> t.blockStatement
    |> functionType id, (head params), _, generator, async
vendethiel commented 6 years ago

In general, f g _ expands to f -> g it, not -> f g it, which is what you seem to expect. So t.blockStatement [_] would expand to t.blockStatement -> [it]. What you want would be composition |> t.blockStatement << Array.of (or just |> Array.of |> t.blockStatement if you ere fine with that).

danielo515 commented 6 years ago

@vendethiel seems that we just posted at the same time, with a very similar solution.

As I said, it is not a very important thing and I'm quite happy with the current solution, it's just I usually prefer literal arrays over functions that builds arrays.

rhendric commented 6 years ago

In my opinion, for this specific case, -> t.blockStatement [it] is the clearest way to express this. _ has the problem, hinted at by ven's comment, that it could be confusing where the implicit function begins—this problem is mitigated by making the placeholder _ only work as the argument of a function application and letting that application be the entirety of the implicit function thus created. It's a limited but easy to understand rule. For that reason, I'm reluctant to let the _ roam further than that. (There's a long history of people proposing various other uses in #19, although beware: some of that conversation refers to outdated LS features.) it doesn't have that problem because the function arrow is explicit.

If the intermediate function in the compiled code, and not the LS syntax, is what bothers you, then perhaps LiveScript should automatically inline functions that are immediately piped to if they meet some simplicity requirements (no variables declared, no use of this (unless it's a bound ~> function) or arguments, maybe some others?), the same way it inlines such functions when written with placeholder _. In other words, perhaps both a |> f _ and a |> -> f it should compile to f(a).

danielo515 commented 6 years ago

Hello @rhendric

As you said, I think the current syntax is good enough, I was just proposing if it were simple, but obviously it is not. One of my main concern however was the usage of unnecessary inline functions as you said. I think this function is simple enough to be inlined. In fact I switched to using Array.of inside the pipe because the compiled code is much simpler and concise.

So, in summary I subscribe your latest proposal