-
Notifications
You must be signed in to change notification settings - Fork 9
Support for arg destructuring #4
Comments
Thanks for the comment. The case of complex destructuring is indeed a tradeoff. Let’s elaborate that last example you gave into the following code. Perhaps we want to inline the function’s first statement into the second statement, while flattening both statements’ nested expressions: async input => {
const { x, y, ...z } = (await preprocess(input)).next();
return (new Foo(x, y, ...z)).log();
} With F# pipes, this would look like: async input => (
input |> preprocess |> await |> v => v.next()
|> ({ x, y, ...z }) => new Foo(x, y, ...z)
|> v => v.log()
) With Hack pipes, you could still use create a unary arrow function that destructures its argument; you only have to explicitly call it with async input =>
input |> await preprocess(#) |> #.next()
|> (({ x, y, ...z }) => new Foo(x, y, z))(#)
|> #.log(); You could also use a async input =>
input |> await preprocess(#) |> #.next()
|> do { const { x, y, ...z } = #; new Foo(x, y, z) }
|> #.log(); But, because of the complex destructuring, this is a case where a single pipeline (whether F# or Hack style) might not be the best choice. The best, flattest most-readable choice might simply be to split the pipeline into two, with a usual async input => {
const { x, y, ...z } = input |> await preprocess(#) |> #.next();
return new Foo(x, y, ...z) |> #.log();
} After all, the point of pipes is that they can untangle without having to name a lot of temporary variables…but, with destructuring, you’re naming a bunch of variables anyway. So perhaps you might as well split the pipeline and use a I’ll keep this issue open for now, in case there’s further discussion to be had about this. |
I don’t think there’s more actionable to do here, so I’ll close it. At most I’d write a section in the explainer saying, “Simple destructuring can be done with properties; complex destructuring probably should be put in a separate pipeline or function anyway.” If there’s more discussion to be had, we can open a new discussion. Thanks! |
Thanks for documenting this. You can also propose alternate syntax e.g. I'd also like to add that the arg-destructuring cumbersomeness is an example of a general downside of introducing new syntax -- that the introducer now has to define how it'll interact with the other language concepts. If existing concepts are used (e.g. single-arg functions already support de-structuring) then this downside doesn't arise. It'd be useful to add a section titled "limitations" or "trade-offs" which mentions this and any other known trade-offs, which may help in evaluating this proposal against other proposals. |
I’m not sure why GitHub deleted my comment, but basically I agreed that I should elaborate more on the trade-offs, which I then did in f53a6e3. I also pointed out both proposals reuse existing language concepts (one uses the expression; one uses the unary function), both with different interactions with other language concepts (like destructuring, function-scoped operations, object/array literals, keywords, etc.). Thanks again! |
While I think it's good to bring up examples where this proposal may be less ergonomic than the alternative, I'm not sure either proposal is necessarily optimal in terms of flatness/readability for this particular example. I doubt we'll end up replacing method chaining in Lodash, jQuery etc. with this syntax, and it's worth remembering that promises already provide a way to keep this kind of pipeline flat and readable, e.g.: input => preprocess(input)
.then(it => it.next())
.then(({ x, y, ...z }) => new Foo(x, y, ...z).log()) |
Note that Hack-style is explicitly not trying to replace method-chaining; it's about bringing the benefits of method-chaining to all other calling styles! ^_^ That way we don't have to go all-in with methods on uber-objects like jQuery does; we can instead use a nice mix of functions and methods as appropriate, like most codebases do today. But existing uber-objects like jQuery are perfectly compatible with Hack-style pipes, and the pipes help in those circumstances when you do have to break out of the uber-object and pass it to a normal function temporarily. |
Sure, but the point is this isn't a compelling example of one of those cases. |
Discusses destructuring too. Closes #4.
I think one difficult case for this proposal would be when args need to be de-structured.
In the alternate https://github.com/valtech-nyc/proposal-fsharp-pipelines/blob/master/README.md proposal, we might write
... |> ({x, y}) => new Foo(x, y) |> ...
but here we'd perhaps have to write
... |> () => { const {x, y} = #; return new Foo(x, y) } |> ...
I've taken a simplistic example but you might have complex de-structuring (e.g
({x, y : [a, ...b], ...z}) => ...
) so we can't just give up on the de-structuring and use?.x
and?.y
instead.Are there any solutions that this proposal can put forward to this problem while preserving the other benefits already outlined by it?
The text was updated successfully, but these errors were encountered: