-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Back to F-sharp #95
Comments
Reminds me of Clojure’s prefix anArray |> ($) { // The $ may be any arbitrary identifier
pickEveryN($, 2),
$.filter(...),
shuffle($),
$.map(...),
Promise.all($),
await $,
$.forEach(console.log)
} A big disadvantage of this temporary-binding proposal as-is is its coupling to variable binding. It cannot be used as an expression itself: this is particularly troublesome with its inability to be used with I don’t know if the Proposal 4 placeholder bikeshedding is a disadvantage that is worth trading for the disadvantage of this proposal. But perhaps it’s worth adding as a fifth proposal to the wiki. |
This may not be the right issue to mention this, but I wanted to mention that I've created an issue on the partial application repo to address the anArray
|> pickEveryN(?, 2)
|> ?this.filter(...)
|> shuffle(?)
|> ?this.map(...)
|> Promise.all(?)
|> await
|> ?this.forEach(console.log(?)); |
I should probably make it more clear by putting it upfront, but the last of the other examples demonstrates how it can be used as an expression. |
Ah, yes, sorry; I see it now. @rbuckton: Cool idea. A prefixed keyword like |
I’d rather see that implemented with const hasAccessByOrgId = do {
let $ = groups.map(g => g.org_ids);
$ = new Set($);
$ = [...$];
$ = $.map(async id => {
let has_access = await Role.hasOrgPermission(id, user.id, perm);
return { id, has_access };
});
$ = await Promise.all($);
objectify($, { by: 'id' })
}; It repeats const hasAccessByOrgId = do $ {
groups.map(g => g.org_ids);
new Set($);
[...$];
$.map(async id => {
let has_access = await Role.hasOrgPermission(id, user.id, perm);
return { id, has_access };
});
await Promise.all($);
objectify($, { by: 'id' })
}; and it isn’t great.) |
@charmander If you're going to repeat var $, hasAccessByOrgId = (
$= groups.map(g => g.org_ids),
$= new Set($),
$= [...$],
$= $.map(async id => {
let has_access = await Role.hasOrgPermission(id, user.id, perm);
return { id, has_access };
}),
$= await Promise.all($),
$= objectify($, { by: 'id' })
); But like @rbuckton mentioned, arbitrary variable assignments are probably difficult for the compiler to optimize. [Edit: grammar] |
A new syntactic form doesn’t have to reuse the same binding. It could internally autogenerate new variable bindings for each step, maintaining monomorphism. Having said that, the biggest problem I have with it is that it mandates a prefix form, rather than it being a compact binary form. But I’ll hold off on further judgement until @mAAdhaTTah and I are able to try implementing some real prototypes. It’d be interesting to try a prototype of this too. |
How would this combine with the pipeline operator, assuming that some steps in your pipeline might involve single-arg functions (ideal for the F#-style pipeline) or multi-arg (better for this proposal)? Lemme try rewriting the Proposal 2 example from the wiki... const($) result = anArray
, pickEveryN($, 2)
$.filter(...)
|> makeQuery
, await readDB($, config)
|> extractRemoteUrl
|> fetch
, await $
|> parse
|> console.log // or , console.log($) if log needs to be bound to console Hmm, assuming all of this works like I think it does, |
@dallonf True, it's possible to combine them that way. My intuition is that the const($) result = anArray
, pickEveryN($, 2)
, $.filter(...) |> makeQuery
, await readDB($, config) |> extractRemoteUrl
, await fetch($) |> parse
, console.log($) Another benefit to |
I think my main concern with this proposal is we end up with two different syntaxes for pipelining. In a hypothetical decision between "F# Pipeline + Temporal Variables" vs. "Hack Pipeline", we might be better off with just "Hack Pipeline"; my intuition is the two syntaxes aren't likely to be used together a lot, although I could very well be wrong about this. If they're intended to be complementary rather than competing for similar use cases, I like this approach, but based on your above comment, if you expect Temporal Variables to be the default, I'm not sure I see the benefit of having both syntaxes. The other concern / question is more political. The pipeline operator has momentum behind it; are you willing to drop pushing for your preferred pipeline solution to throw your support behind a brand new alternative with no support yet? I could see this ending up with only F# Pipeline landing and Temporal Variables stalling, unless the two proposals are tethered in some way. Is that how we intend to present them? Just to throw it out there (and this could be terrible) but picking up on @js-choi's suggestion above, maybe something like this would be worth exploring? path
|> x => x.trim()
|> ($) {
$ ?? throw new TypeError(),
await fetch(`https://example.org${$}`),
}
|> x => x.text()
|> console.log; This explicitly introduces the placeholder variable into the pipeline and allows the developer to choose its name. |
I had been exploring similar parameterized-block syntaxes while thinking about the Proposals and writing Proposal 4’s spec. They may be useful for Proposal 1 but not for Proposals 2,3,4. In general: Parameterized blocks are similar in functionality to arrow functions as expressions and Proposal 1: The parameterized block is useful for F-sharp Style Only pipes in order to perform function-scoped operations. Its functionality otherwise overlaps with using arrow functions as pipeline bodies. Proposal 2,3,4: The parameterized block in the example above is redundant with simply flattening the block’s contents into the pipeline thread, because |
I think this is a valid concern. Perhaps it's not so bad if we take the differences into deeper consideration:
I do think they're complementary. If it turns out they are not used together a lot, but still used separately, I think that means they serve different use cases.
Yes, I'm ok with that. IMO the presence of the Temp Bindings proposal will serve to absorb shock from questions about async and multi-arg function solutions; it will allow With all that said, Proposal 4 with a |
Given that I expect F# pipelines to use inline arrow functions, I don't see the differences you suggested to be that great, which means I'm inclined to think the differences in usage is likely to be a result of a difference in style preference than one being better than the other. I think we more fundamentally disagree on the ergonomics of arrow functions in the pipeline, which I think is driving our different preferences, although the placeholders do solve the "ad-hoc operator" problem.
Is this to suggest, with these two proposals, F# would / should not ship w/ the "inline await" rule, instead absorbing that use case in to Temp Bindings? |
Yes, this is probably it. Personally I don't mind typing parenthesized arrow functions with
Not would or should, but can. Just throwing it out there as a suggestion. |
I'm still hopeful this is a potential solution for parenthesized arrow functions. Would it still be a burden (in your opinion) if they didn't need to be paren'd? |
I think would help if it doesn't introduce other confusions. For example, one issue the linked comment pointed out is you need to use parens to use const foo = x => (x |> bar |> baz) // works
const foo = x => x |> bar |> baz // doesn't work? But it any case a good solution like that would make things more palatable, even if not as streamline as the comma operator. |
Closing this issue, as the proposal has advanced to stage 2 with Hack-style syntax. |
Inspired by this comment I've pushed up a new proposal: Temporary Bindings. This new proposal seems to be a superior version of the Hack-style proposal, and without using the pipeline operator token.
What this implies is, if this new proposal were to be adopted, then the "pipelining arbitrary expressions" use case would be covered, leaving room for the pipeline operator to revert back to its original F-sharp behavior.
Thoughts?
The text was updated successfully, but these errors were encountered: