01:44
<rbuckton (PTO: 7/5 - 7/16)>

Could we, perhaps, appease the functional programming folks by using |> for Hack pipes, ||> for F# pipes, and |||> for function composition? i.e.,

  • x |> expr - First-order pipelining, i.e. evaluating expressions (i.e., "evaluate pipeline" or "expression pipeline"). Has a topic.
  • x ||> F - Second-order pipelining, i.e. calling unary functions (i.e., "call pipeline" or "function pipeline"). Does not have a topic.
  • F |||> G - Third-order pipelining, i.e. composition of unary functions (i.e., "compose pipeline"). Does not have a topic.

This makes some sense as far as first-order vs. higher-order pipeline:

// first-order
x |> F(%) |> G(%) 

// second-order
x ||> F ||> G

// third-order
// NOTE: this is backwards from f . g === g(f(x)), but perhaps we 
//       could introduce a "back pipe" `<|||` as a true point/compose operator
x ||> (F |||> G)
x |> (F |||> G)(%)
01:46
<rbuckton (PTO: 7/5 - 7/16)>
That would not only cover the FP crowd but also the data science crowd.
01:48
<rbuckton (PTO: 7/5 - 7/16)>

And if we wanted true point/compose, we could pair it with "back pipes" like in F#:

G(%) <| F(%) <| x // trickier to parse though
G <|| F <|| x
(G <||| F) <|| x
01:50
<rbuckton (PTO: 7/5 - 7/16)>

The back-pipe variant has similarities to decorator application as well:

class C {
  @F
  @G
  method() {}
}
 
F <||
G <||
function () {}
02:04
<rbuckton (PTO: 7/5 - 7/16)>
Then there's something for everyone, and while there might be some that ask "But can't we use |> for the F# case?" we could say that |> means "first-order", ||> means "second-order" and |||> means "third-order" as a heuristic roughly based in first-order vs. higher-order logic.
02:13
<rbuckton (PTO: 7/5 - 7/16)>

For example, suppose we had my functional operators and PFA proposals:

a * b + c
a {*} b {+} c 
plus(times(a, b), c)

// first-order
a |> % * b |> % + c
a |> % {*} b |> % {+} c
a |> times(a, b) |> plus(%, c)
 
// second-order
a ||> {*} b ||> {+} c
a ||> times~(?, b) ||> plus~(?, c)
 
// third-order
a ||> ({*} b |||> {+} c)
a ||> (times~(?, b) |||> plus~(?, c))
02:43
<TabAtkins>

Did we ever consider * as a topic token? f() |> g({ x: * }). I don’t remember why * was disqualified, if it was at all.

(Otherwise, the top runner is ^^, given @’s recent problems found by WH.)

Binary operators are all problematic, and common ones like * are bad separately bc it's not unlikely that authors will multiply the topic by something.
02:44
<TabAtkins>
But yeah, * vs ** and yield* all make * even worse, specifically.
02:50
<TabAtkins>
rbuckton (PTO: 7/5 - 7/16): Putting aside that I'm very confident a second pipe won't make it thru committee (the majority of the benefit of pipeline is already granted by the first one), I've no problem with pursuing tacit pipes in the future, so long as they don't block this proposal (either by trying to claim syntax, or by getting bundled into this proposal as a package).
02:56
<rbuckton (PTO: 7/5 - 7/16)>
It might have more credence if its promoted as a compromise for FP/Data Science, and that it may have less impact on performance if a first-order |> already exists, i.e.: The folks that need it will use it, as they would have been using unary functions anyways (so perf impact would be equivalent), but general use cases could lean on |> instead. Linters could, in theory, point users to using |> over ||> (or |||>) when their usage would benefit from simplification.
02:57
<rbuckton (PTO: 7/5 - 7/16)>
And it would be a way to include, rather than alienate, existing FP libraries in the ecosystem.
03:05
<rkirsling>
while any empirical data would obviously confirm that * is more widely used than ^, I will never not hate that ^^ is in the running just because we consider ^ uncommon in practice
03:05
<rkirsling>
I've said that here before but I guess I should say it more publicly/formally
03:18
<TabAtkins>
Like I said, I've no problem with it so long as it doesn't do anything to block this one; promoting it as a compromise solution implies that it's a package with the current pipeline and will only hurt our chances of getting either thru.
12:38
<jschoi>

Yeah, agreed, but I think that requiring separation between the topic and binary */** might not be a big cost.

I suspect maybe >90% of uses of pipe will be with function calls or object/array/tuple/record literals f() |> g(#[*], 0), and it may be worth optimizing readability for those cases, rather than for f() |> * ** 2. And even f() |> * ** 2 isn’t that unreadable, I think.

It’s worth at least considering, since it probably is basically the only single-character token left.

12:39
<jschoi>
You dislike ^^ then, is that right, Ross? If so, I’ll add it to the wiki page’s Table of Opinions. 🙂
13:02
<Ashley Claymore>
I'm presuming ~ was discussed? It's at least only a unary operator and not binary. And uncommon
13:03
<Ashley Claymore>
I guess ~(1) is visually ambiguous
13:06
<jschoi>

~ is nice and we haven’t discussed it properly either. f() |> g(#[~], 0) |> ~(~), hmm…

PFA syntax would use ~. But f() |> (~)~(0, ?) would be quite rare!

13:08
<jschoi>
(I’ve added a breakout session for topic tokens to the plenary although we might not have time.)
13:08
<Ashley Claymore>
To call it would have to do something like: (~)(arg)
13:09
<jschoi>
Oh yeah. Which might not be that bad…?
Which is worse (or at least which would be more common):
f() |> * ** 2f() |> (~)(2)
13:11
<Ashley Claymore>
could be a hazard if someone writes ~(arg) and expects a function call. If arg can be converted to a number it won’t throw either
13:12
<Ashley Claymore>
but again, maybe that’s OK for the benefit of a single character token
13:13
<jschoi>

At least it will throw if it’s the only expected use of topic in the pipe body.

f() |> ~(2) would be a SyntaxError: “Pipe body contains no topic.”

(f() |> #[~(2), ~ + 1] would not be a SyntaxError, but is that situation going to be common? Hmm.)

13:19
<Ashley Claymore>
Ah yes. I had completely forgotten about "it will throw if it’s the only expected use of topic in the pipe body."
13:52
<TabAtkins>
* * * ** *
13:54
<jschoi>
That example makes my eyes feel like they’re bleeding, but at least that’s hardly ever going to happen. R-Right?
13:55
<TabAtkins>
I'm just saying, math is a lot more common than binary xor 😅
13:56
<jschoi>
Yeah, true. Though…I think was RBN or WH or someone who said that we can always come up with contrived examples, like #[#[#[#[##, this.#blah, #{#[this.#a, this.#b, ##]}], ##]]].
The key is whether an example is going to be common enough to be a concern.
Frequency × impact per occurrence = expected impact.
13:56
<jschoi>
It’s true that the frequency of binary * and ** > the frequency of prefix ~
13:57
<jschoi>
…Will the frequency of calling-a-topic-as-a-function be comparable to the frequency of binary * and **? It is tough to say.
14:47
<TabAtkins>
Right, my big concern is that multiplication is pretty common (and I've wanted to use pipe in precisely a spot where I'd multiply the topic). Xoring the topic, or calling as a function, are both way less common, yeah. (And outside of lambda calculus, calling the topic as a function and using the topic as an argument would be incredibly rare, so avoiding the early error will be super uncommon.)
14:47
<TabAtkins>
I also don't feel good about the yield* case
14:48
<jschoi>
Sounds reasonable! I think I agree that topic ~ is probably better than topic *. (And it should still work with PFA syntax: f() |> g~(~, 0).) I will add these opinions to the wiki table when I can.
21:17
<rkirsling>
yeah, I think I'd feel less bad about ~
21:17
<rkirsling>
to be clear, I'm not blocking wrt ^^, it just makes me slightly sad (...which is ironic when viewed as kaomoji lol)
21:24
<rkirsling>
I say "slightly" because re-noticing in the wiki table that empirical rarity is not the only justification for ^^ makes me feel less awful
21:30
<jschoi>
I’m not sure why I thought * would be any different from % and ^, which Shu and others are against because they would require InputElementDiv/InputElementRegExp–style contextuality in the lexer. * is not different from % and ^; it suffers from the same problem, and so * is out.
I don’t think ~ suffers from the same problem? I left a comment with some examples but I’m not 100% sure.
22:11
<TabAtkins>
Apologies, I thought you'd already noticed that - I referenced the contextuality problem indirectly in my first response to *.