Skip to content
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

Vote to drop the smart operator from this proposal #158

Closed
babakness opened this issue Nov 10, 2019 · 37 comments
Closed

Vote to drop the smart operator from this proposal #158

babakness opened this issue Nov 10, 2019 · 37 comments

Comments

@babakness
Copy link

Can there be a community vote on this and we move forward?

I'm on team F# because it works well with the JavaScript we have today. A future partial application addition to the language would work well with the F# proposal and so would a placeholder proposal.

I feel that the Smart operator is a "revolutionary" approach where as F# style operator is "evolutionary". The problem I see with the former is that it is embedding a parameter placeholder proposal within it and in such a way to warrant conversations outside of the scope this proposal.

I don't doubt that having parameter placeholders would make Point-Free programming more readable, especially when not paired with a static type system like TypeScript; but that is a separate problem to address. We can have a proposal where this possible

<Component onClick={ handler( #, user ) } >

It is independent of a pipeline operator. When we have it, it will work with the F# proposal just fine. Right now it feels like the Smart proposal is blocking.

My vote is to remove it from this proposal and break it up into different proposals.

let foobar = foo |> bar
let foobar =  x => x |> foo |> bar

which just means

let foobar = bar( foo )
let foobar = x => bar( foo ( x ) )

That's it! Simple, handy, and easy to understand

Separately, we can have some placeholder proposal

let identity = # 
let half = divide( #, 2 )
let fifty = 100 |> half
let ten = 100 |> divide( #, 10 )

would mean

let identity = x => x
let half = x => divide( x, 2 )
let fifty = half( 100 )
let ten = ( x => divide( x, 10 ) ) ( 100 )

or whatever it is, it is independent.

I appreciate the desire to discourage point-free in some codebases and to enforce placeholders; I see that easily solved with linters.

@mikestopcontinues
Copy link

I would hate to give up the ease of use afforded by the smart proposal (e.g. #?.propName), but I do see splitting the proposals as the best way forward. Everyone I show the pipeline operator loves it. It shouldn't be this hard to move the proposal forward.

@phaux
Copy link

phaux commented Jan 2, 2020

I'm pretty sure you mean the "minimal" proposal, not F#.

I pretty much agree. There's already such a accompanying proposal: partial application proposal

There could be also a proposal which does property access and we would end up with basically the same functionality as smart pipeline, except more universal, because it could be used e.g. with array.map and JSX event props.

@aadamsx
Copy link

aadamsx commented Apr 3, 2020

According to @ljharb (I guess he's TC39) we have to make the ironclad case that the F# Style, and not the Smart Style, is the only one that make sense -- forever -- smh

From his post:

it also has to prepare an ironclad, persuasive argument for why that shape is the only one that will make sense forever (including outlining potential extensions, or which extensions are precluded).

How can we prove a negative? How can we prove that the Smart Style is not a valid choice -- to some joe blow with an opinion?

I don't see how we get past this enpass with this mentality smh

@ljharb
Copy link
Member

ljharb commented Apr 3, 2020

That's how every addition to JavaScript works - it's always forever, because we can never break the web, so it's always a bad idea to rush into anything.

I don't see how we get past this impasse with this mentality smh

Perhaps you're starting to appreciate why proposals can sit, for years, with no progress - not because people are shirking their duties, but because often there's unknowable questions, and doing nothing is highly preferable to doing the wrong thing?

@aadamsx
Copy link

aadamsx commented Apr 3, 2020

That's how every addition to JavaScript works - it's always forever, because we can never break the web, so it's always a bad idea to rush into anything.

It's hyperbole to suggest moving forward in the F# style will break the web. And a proposal that will help bring JS into the functional era, after two years, still without a decision on the style is not what I would call rushing smh

Perhaps you're starting to appreciate why proposals can sit, for years, with no progress - not because people are shirking their duties, but because often there's unknowable questions, and doing nothing is highly preferable to doing the wrong thing?

The only reason this has not seen progress is because this proposal has not dropped Smart style long ago and focused efforts on cleaning up remaining items on the F# side. Also going with the F# style for pipes is not what I would call a wrong choice. Just read the head post on here, he sums things up nicely.

@ljharb
Copy link
Member

ljharb commented Apr 3, 2020

What I mean is, if we move forward with F# style, and in 1, 5, 10, 50 years, we decide it was the wrong choice, we're stuck with it forever.

The only reason this has not seen progress

I don't think that's a statement that any non-delegate can possibly be a credible expert on - and I doubt even a delegate could claim that authoritatively.

@KristjanTammekivi
Copy link

Safe to say there is no ironclad right answer here, the pipelines eventually do the same thing. F# promotes currying and Hack promotes partial application. Both of them have been proven to work in other languages so let's just write up the exact differences, pros and cons, interactions/conflicts with other proposals (partial application) for both and just have the committee have a vote on the matter.

@mAAdhaTTah
Copy link
Collaborator

@KristjanTammekivi The committee itself doesn't function by vote but by consensus. The goal then is not to write up the differences & let them vote but to convince those who are opposed to the pipeline operator or prefer a different alternative that your preference is best and worth proceeding with.

@tabatkins
Copy link
Collaborator

The only reason this has not seen progress is because this proposal has not dropped Smart style long ago and focused efforts on cleaning up remaining items on the F# side.

...do you not see how this statement is exactly mirrorable for the other side? If we're pretending that it's only the objections of the topic-style-likers that are preventing pipeline from being added to the language, then equally if the F#-style-likers dropped their objections and embraced topic-style we could have it in just as easily.

Neither is true, of course - the committee does not yet have consensus that either (or any) pipeline syntax is worth adding to JS; new syntax is always a very hard sell because JS only has a limited amount of syntax to use, and every addition reduces the set of future possible syntaxes we have available to us, both in technical terms (can't introduce new ambiguous syntax) and practical terms (JS shouldn't become Perl).

(I myself am frustrated at this, fwiw - I think pipeline would make an excellent addition to the language if done properly. "Properly" here means "topic-style" as far as I'm concerned, however; I don't think F#-style brings enough to the table to be worth adding. That all said, this is a topic I want to put more effort into championing in the near future.)

@rkirsling
Copy link
Member

It's hyperbole to suggest moving forward in the F# style will break the web.

Just to clarify, @ljharb isn't saying that this proposal in either form is bad for the internet or something. 😄

One of the guiding principles of web standards is "don't break the web": the changes we make must be compatible with the web as it already is, warts and all. Being subject to this principle makes JavaScript unique as a programming language, since it means we generally can't deprecate anything.

If the F# proposal were to make it to the end of the TC39 process, that certainly shouldn't "break the web", as we should have already verified its web compatibility during Stages 2 (in spec text) and 3 (in browsers). What it would do is become part of the web that we're obliged not to break. That doesn't mean we should try to predict what killer ES2030 feature we might be inhibiting, but it does mean that we have to take the little details seriously, because we carry their repercussions with us for as long as the web lives on.

@aadamsx
Copy link

aadamsx commented Apr 4, 2020

The goal then is not to write up the differences & let them vote but to convince those who are opposed to the pipeline operator or prefer a different alternative that your preference is best and worth proceeding with.

@mAAdhaTTah who is opposed to the pipeline operator or prefers something else, and most importantly why (or do you know)

@mAAdhaTTah
Copy link
Collaborator

@aadamsx First, @tabatkins, who posted in this thread, is on the committee and prefers Smart Pipeline. If you search for him in this repo, he's made his arguments around here.

There are two threads (one, two) on preference for the bind operator for this kind of pipelining (I believe OP on the second one is a committee member).

The proposal champion has suggested exploring Elixir style pipelines.

I've heard there are some committee member who don't think any of these are necessary additions to the language (I'm not sure who they are, to be honest), nor am I sure of their specific objections (@ljharb has shared general objections for adding too many proposals in conversations with you, so could be related to that).

I hope that provides some context. There's still a lot of ground to cover.

@mikestopcontinues
Copy link

I wish there was a way to better organize the discussion around this. Maybe a state-of-the-union doc? Maybe close some issues? The elixir thread suggests there really isn't any interest in exploring that avenue, yet the issue is still open.

@littledan
Copy link
Member

Personally, I'm in favor of moving ahead with the F# proposal (or somewhere between the F# proposal and minimal proposal). I think we can firmly conclude that, if we want to have partial application, it will need to be an independent, first-class feature, not limited to pipeline contexts like the "smart" proposal does--it's just too confusing for the thing after the |> to not be an arbitrary expression. I think the F# proposal will be very useful on its own, and partial application points the way to improvements that can be done within that framework, but is not necessary.

@mlanza
Copy link

mlanza commented Dec 23, 2020

What I mean is, if we move forward with F# style, and in 1, 5, 10, 50 years, we decide it was the wrong choice, we're stuck with it forever.

The belief that all viewpoints are equally valid is a problem with design by committee. I've participated in bureaucracies who after years were unable to act because they too believed in consensus. A committee is most effective when it has a trusted chair who ultimately owns the mission and is empowered to decide.

Consensus doesn't trump time. No one would argue that if we reached consensus in 10 years, that would be better than deciding sooner. And consensus doesn't guarantee no regrets.

Clearly there are bad choices, but after this much deliberation the bad choices have been highlighted. All that's left is trade-offs. I was originally a proponent of smart pipelines, but I now see that with partial application syntax F# style pipelines do the job sufficiently—and they're well established and proven.

I'm in agreement. Drop the smart operator if this helps the proposal advance.

@theScottyJam
Copy link

theScottyJam commented Jul 17, 2021

This discussion seems to be focused on implemented F#-style pipes, and adding a way to do partial application anywhere after-the-fact. It's a good idea, but this is not the same as the hack-style pipes proposal. A globally available partial application syntax would have problems such as:

  • How do you define the boundaries of this partial application? In x |> ? + 2 why is that equivalent to x |> _ => _ + 2 and not _ => x |> _ + 2? What set of rules do we follow to decide what goes in the function definition and what stays out? Are these rules intuitive? Currently, the partial application proposal resolves this issue by restricting "?" to only be used in an argument list - this is a lot more restricted than what the current hack-style proposal gives us.
  • Because it's all F# style pipes, you would still need some way of doing an await version of the pipe. e.g. x await |> await ? + 2 - and that looks pretty gross.

The current hack-style pipes proposal does not have these kinds of issues. This means, even if we decide to follow this route, it's vital that we decide now that hack-style pipes are not the path we want to take, because as soon as we add F# pipes, the current hack-style idea will not be an option.

So you can hate me for saying this, but going this route won't speed the proposal up. We still have to eliminate all alternative options. And I personally hope that TC39 doesn't rush this kind of decision.

@mAAdhaTTah
Copy link
Collaborator

Because it's all F# style pipes, you would still need some way of doing an await version of the pipe. e.g. x await |> await # + 2 - and that looks pretty gross.

This is part of the F# proposal:

url
  |> fetch
  |> await
  |> x => x.json()
  |> await

I personally hope that TC39 doesn't rush this kind of decision.

We've been discussing this proposal for, I think, 4 years now. Nothing is being rushed 😄

@theScottyJam
Copy link

theScottyJam commented Jul 17, 2021

It's been a while since I looked at the syntax for the F# proposal - this has been sitting around for a while :), so I misremembered how the proposed await-pipeline syntax was supposed to be done.

But yeah, this example should highlight the issue I was explaining:

// hack-style
x |> await f(?, 2)

// F# style
X |> _ => f(_, 2) |> await

// F# style + current partial application proposal
x |> f(?, 2) |> await

This is why we can't just "rush" F# style pipes in and tack on partial application afterwards. And thank goodness it's not being rushed. (but ... it is pretty slow-going waiting for this all to get figured out ... *chirp* *chirp*)

@mikestopcontinues
Copy link

@theScottyJam I suspect someone writing a new minimal proposal could get the ball rolling again. It seems clear that even if you drop the awaits and go real minimal, you still have a very valuable language feature.

@babakness
Copy link
Author

babakness commented Jul 17, 2021

IMO the await keyword in pipe, no matter how it is supported, is a bad pattern.

It introduces a side effect into function composition.

What if the await fails? This should be handled either the classical imperative way (ie try/catch) or through the use of a Task or some Monad / Algebra pattern. The standard Promise where you chain then (where the second parameter handles the error) and have a general catch is fine for this as well.

I'd go so far as raising a syntax error when using await in a pipe.

@lightmare
Copy link

It introduces a side effect into function composition.

It's not the await that introduces side effect, but a non-pure function. (any function, not just async ones)

@theScottyJam
Copy link

theScottyJam commented Jul 17, 2021

What if the await fails? You add a .catch() handler to the promise if you have some specific way to handle the failure. If the failure is unexpected, then just let it throw, and don't add a .catch() handler.

const roles = user
  |> getUserId(?)
  |> await fetchGroups(?)
    .catch(err => /* handle it if you can */)
  |> rolesFromGroups(?)

The catch handler can be defined outside the pipeline and passed in, if you find that that reduces clutter.

You can even define general-purpose helpers:

async function handleError(fn, { type, handler }) {
  try {
    return await fn()
  } catch (err) {
    if (err instanceof type) {
      return handler(err)
    }
    throw err
  }
}

const roles = user
  |> getUserId(?)
  |> await handleError(fetchGroups, { type: NotFoundError, handler: () => [] })
  |> rolesFromGroups(?)

This topic on the TC39 form has discussed other syntax ideas to make error handling easier in the middle of a pipeline operator.

But in short - this shouldn't be any more of an issue then using await inside of something like this:

const roles = rolesFromGroups(await fetchGroups(getUserId(user)))

@mAAdhaTTah
Copy link
Collaborator

I suspect someone writing a new minimal proposal could get the ball rolling again. It seems clear that even if you drop the awaits and go real minimal, you still have a very valuable language feature.

My memory is we added await support to the F#-style pipeline explicitly because the committee believed it needed to be supported, so I don't think this is generally true.

@mikestopcontinues
Copy link

@mAAdhaTTah Interesting. I see how that makes sense, the idea being a 1:1 between nested function calls and pipeline. Unfortunately, this is one of the areas where smart is clearly better than F#, so I would rather wait until we know about placeholders to implement it.

@davidvmckay
Copy link

davidvmckay commented Jul 23, 2021

@theScottyJam

  • How do you define the boundaries of this partial application? In x |> ? + 2 why is that equivalent to x |> _ => _ + 2 and not _ => x |> _ + 2?
valueExpression = (* https://tc39.es/ecma262/#sec-ecmascript-language-expressions *)
variableName = (* https://tc39.es/ecma262/#sec-identifier-names *)
functionBlock = (* https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations *)
HackShorthand = ... (* HackShorthand is merely sugar for a unary fat arrow with a convention-based argument name, e.g. `# + 2` or `? + 2` become `X => X + 2` *)
namedFunction = "function" , variableName1 , "(" , variableName2 , ")" , "{", functionBlock , "}"
anonymousFunction = "function" , "(" , variableName , ")" , "{", functionBlock , "}"
fatArrowFunction = "(" , variableName , ")" , "=>" , valueExpression
                               |  variableName , "=>" , valueExpression
variableContainingUnaryFunction = valueExpression (*such that the type of the expression's value is callable... and the developer intends for the callable value to receive a single argument as a valid use case.*)
unaryFunction = namedFunction | anonymousFunction | fatArrowFunction | variableContainingUnaryFunction | HackShorthand
pipelineOperator = "|>"

pipelineExpression = valueExpression pipelineOperator unaryFunction

(* evaluation of pipelineExpression is a valueExpression equivalent to the return of the RHS unaryFunction when applied to the LHS argument ... this allows pipelineExpressions to be chained without using a sea of parentheses, such as in existing function application syntax, and without using objects to "dot off of" for "fluent syntax" -- this is the basic usefulness proposition of |> operators. *)

RHS of |> is always a unaryFunction applied to valueExpression on LHS , i.e. pipelineOperator === functionApplication of RHS to argument of LHS.

Thus, _ => x |> _ + 2 could never be a valid expansion of HackShorthand because _ + 2 is NOT a unaryFunction, but a valueExpression. (true, a unaryFunction IS a valid valueExpression, but it is only a narrow subset of valueExpression, because it is callable, and expects a single argument. no guarantees are given nor required about the return value of the unary function in the general case.)

(await, as I see it, is just a unary function that unwraps a Promise. And no, not a PURE function, nor should it be, since in general, JavaScript functions are NOT pure, and any of them can throw errors and disrupt control flow at the point of function evaluation.)

Can you elaborate on a couple examples in which HackShorthand as a sort of sugar for fat arrow functions could not be used orthogonally with pipelineExpression as defined above to achieve everything that an intertwined "hack style pipeline" might? Put another way, I can't see how an intertwined "hack style pipeline" is any different from "hack style unary function" plus "function application operator".

@mAAdhaTTah
Copy link
Collaborator

HackShorthand is merely sugar for a unary fat arrow

This is not true and this common confusion is my biggest concern about Hack pipelines in general.

@davidvmckay
Copy link

HackShorthand is merely sugar for a unary fat arrow

This is not true and this common confusion is my biggest concern about Hack pipelines in general.

What am I missing then? It definitely LOOKS to me like we can apply a desugaring rule and an arbitrary generator of unique variable identifiers to expand any Hack Style pipeline expression to a combination of functional application and unary function. A pipeline could even be desugared to just a giant nest of function calls

url
  |> # + '?query=foo'
  |> fetch
  |> await
  |> x => x.json()
  |> await

// becomes
await (
  (x => x.json()) (
    await (
      fetch (
        (x1 => x1 + '?query=foo') (
          url
        )
      )
    )
  )
)

@ljharb
Copy link
Member

ljharb commented Jul 23, 2021

@davidvmckay an await or a yield inside the pipeline relates to the function containing the pipeline. your desugaring breaks that.

@mAAdhaTTah
Copy link
Collaborator

mAAdhaTTah commented Jul 23, 2021

In addition, you example is wrong:

url
  |> # + '?query=foo'
  |> fetch
  |> await # // needs a placeholder here
  |> (x => x.json()) // this would need parens but can also just be #.json()
  |> await # // and here

and can be simplified:

url
  |> await fetch(# + '?query=foo')
  |> await x.json()

@theScottyJam
Copy link

theScottyJam commented Jul 23, 2021

That grammar seems much more restrictive than how I currently understand F#. Right now, you're saying on the RHS of the pipeline operator, there must be a unaryFunction, which is one of these: namedFunction | anonymousFunction | fatArrowFunction | variableContainingUnaryFunction | HackShorthand. If I understand that correctly, that would mean the following is a syntax error x |> f(2) (where f(2) returns another function) - that's not how the current F# proposal works.

If you added support for function calls, then what would happen with this: x |> g(f(2, ?))?

  • In hack-style pipelines, I would expect that to be equivalent to this in F#-style x |> _ => g(f(2, _)).
  • If ? was a globally understood token for partial application, then I would expect it to desugar to x |> g(_ => f(2, _)), just like the current partial-application proposal plans on doing it.

Ultimately, if you think you've found a way to make a more powerful and intuitive partial expression syntax, I would bring it up on the partial application proposal because right now, they're thinking of having heavy restrictions on it (limiting the "?" token to just within a function parameter list), because it's difficult to draw boundaries for a partial expression. This issue in particular addresses some of those problems in more detail.

@mAAdhaTTah
Copy link
Collaborator

If I understand that correctly, that would mean the following is a syntax error x |> f(2) (where f(2) returns another function) - that's not how the current F# proposal works.

This is correct – the Hack style is intentionally restrictive to avoid footguns. A placeholder is required on the RHS of all pipelines in Hack style.

@tabatkins
Copy link
Collaborator

Yeah, note that x |> f(2), if it were allowed in Hack-style, would just evaluate to f(2), ignoring the piped-in x value entirely. It's not really a pipeline at that point; at best it's two separate pipelines.

In the rare case you do want to completely ignore the piped-in value and continue on with something entirely new, the comma operator can do it: x |> #, f(2) satisfies the syntax constraints and evaluates to f(2).

(Swapping the order around the comma is also how you can insert side-effects into the middle of a Hack pipe easily; x |> console.log("here"), # evaluates the console.log() but continues the pipeline without changing the value.)

@davidvmckay
Copy link

I am expecting that value1 |> value2 is always strictly equivalent to value2(value1) regardless of the way expressions are resolved to values. I Just want to liberate the |> operator discussion, because I really can’t see how it is intrinsically tied to any flavor of rules for how either expression1 or expression2 may be parsed to functions or not, it still comes out in pseudocode as someValueOfAnyType |> someFunctionWrittenAnyWay —> (someFunctionWrittenAnyWay)(someValueOfAnyType) —- and the transformation is completely reversible.

@theScottyJam I made a mistake in my EBNF, i meant to allow RHS to be ANY expression where evaluation according to standard precedence rules yields a value of callable type. 123 |> curriedAdd(13) should be totally legal if function curriedAdd(addend) { return function(addendum) { return addend + addendum; } } — otherwise, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function

@ljharb yes, the await operator works because the (implied) containing function is a sync — in either syntactic form. @ljharb , @mAAdhaTTah a further set of equivalent expansions:

const myFunc = async url => url
  |> ? + '?query=foo' // @theScottyJam using the `?` character instead of `#`, but I mean them the same way: contextual argument name placeholder.
  |> fetch
  |> await
  |> x => x.json()
  |> await
;

// and here is a simpler, equivalent definition that just makes different artistic choices about how to form RHS unary functions
const myFunc = async url => url
  |> await fetch(# + '?query=foo')
  |> async x => await x.json()
;

// becomes
const myFunc = async url =>  await (
  (x => x.json()) (
    await (
      fetch (
        (x1 => x1 + '?query=foo') (
          url
        )
      )
    )
  )
);

/////////////
// or even becomes
const myFunc = (url) =>  fetch (
    // note this is immediately executed fat arrow function definition applied to url
   ((( // demonstrating parentheses are not always special to function definition, such as forcing a different order of evaluation
    ((x1) => x1 + '?query=foo') 
    )))
    // the parentheses around argument lists for function invocation are different than parenthetical value expressions
    (url)
   // will now fetch `${url}?query=foo` and return a Promise…
  )
  .then((x) => x.json()) // x is the value of the Promise returned by fetch; assuming json parsing returns a Promise, then:
  .then((x) => x) // totally superfluous, just like the outer await was in the earlier examples.
// It unwraps the Promise, but there’s no point because then it just returns the value…
// which in an async function, is immediately re-wrapped in another Promise
// for further up the call stack to deal with.

@theScottyJam regarding how to disambiguate scoping to decide how to bind a placeholder argument name while desugaring to an anonymous function definition, I would apply some kind of precedence rule like “Just go right on up until you hit either invocation of the expression like a function (whether with parents or pipeline or call / bind etc) or until assigning the expression to a variable, and then prefix the implied arg list and fat arrow.”

Also, I agree with @tabatkinsx |> f(2) could be solved with comma if we really want do something superfluous like that. It should throw unless the result of f(2) is callable — otherwise |> in that example contributes nothing apart from syntactical distraction.

@theScottyJam
Copy link

theScottyJam commented Jul 24, 2021

regarding how to disambiguate scoping to decide how to bind a placeholder argument name while desugaring to an anonymous function definition, I would apply some kind of precedence rule like “Just go right on up until you hit either invocation of the expression like a function (whether with parents or pipeline or call / bind etc) or until assigning the expression to a variable, and then prefix the implied arg list and fat arrow.”

Well, then that's should be the example you were looking for with how F# pipes + partial expressions differ from hack pipes. x |> f(g(#)) would mean two different things, depending on which route we take, and if we take the F# route, there's no reasonable way to emulate hack-pipe afterwards. We've gone down a one-way road.

I think your proposal also doesn't address async/await very well. Take this example:

x |> await f(#, 2) |> g

We're bounding the # to the nearest call, so it should turn f(#, 2) into _ => f(_, 2), leaving us with this:

x |> await (_ => f(_, 2)) |> g

This is not what we were trying to do. We're awaiting a function instead of a promise!

Edit: I thought of a couple more examples of something where F# + partial expressions differ from hack pipes:

  • key |> [map.get(#)]. In F# + partial expressions, this would mean key |> [_ => map.get(_)], i.e. the array contains a function, not a map's value, which is illegal because arrays aren't callable.
  • x |> f(#, #). With partial expressions, is this a syntax error? A curried function? Or a function that takes two parameters? The current partial application proposal interprets this as a function that takes two parameters, which is not how hack-pipes takes this.
  • x |> (() => #) A confusing example, but still shows a difference. hack-style would cause this to be equivalent to () => x while partial expressions would (I presume) cause this to be the same as (() => _ => _)(x)

(In writing all of these examples, I've learned that I really dislike the ? sigil for a placeholder - the ? just has too many meanings already)

@lightmare
Copy link

I am expecting that value1 |> value2 is always strictly equivalent to value2(value1) regardless of the way expressions are resolved to values.

That's essentially the "minimal" proposal, i.e. no await/yield.
If you just call whatever the RHS evaluates to, then you cannot desugar this:

Fsharp = input |> foo |> await;
Hack = input |> await foo(#);

I Just want to liberate the |> operator discussion, because I really can’t see how it is intrinsically tied to any flavor of rules for how either expression1 or expression2 may be parsed to functions

Then please consult #83 and #104. Long discussions showing that the F# flavour has intrinsic parsing issues that need to be solved.

Anyway, this topic — vote to drop the smart operator — is over. It has already been withdrawn.

@mAAdhaTTah
Copy link
Collaborator

Anyway, this topic — vote to drop the smart operator — is over. It has already been withdrawn.

Technically, yes, but Hack has been introduced to "replace" it, so the impasse described in the OP is still present.

@tabatkins
Copy link
Collaborator

Smart operator dropped, and replacement impasse resolved (by the committee advancing this proposal to stage 2 with the Hack syntax), so closing this issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests