We recently discussed introducing a |> operator that works like the current -> binary operator, and deprecating the current a->b syntax so that we can change what it means in the future.
As of this PR we now have the new syntax, and the formatter automatically changes from the old syntax to the new one.
A future PR will change how -> works, but in the meantime, roc fmt should upgrade you.
I just ran the new formatter on all the Exercism exercises, and it worked fine overall, except for one thing: it replaces a->f()? with a |> f? which causes a TYPE MISMATCH error. I've filed issue #10510.
Also, there's weird stuff going on with sequences of pipes. For example, this code:
expect {
_result = CircularBuffer.create({ capacity: 3 })
.write(1)?
.write(2)?
.write(3)?
.read()?
-> expect_value(1)
.write(4)?
.overwrite(5)
.read()?
-> expect_value(3)
.read()?
-> expect_value(4)
.read()?
-> expect_value(5)
Bool.True
}
main! = |_| { Ok({}) }
Is formatted like this:
expect {
_result = (
(
(
CircularBuffer.create({ capacity: 3 })
.write(1)?
.write(2)?
.write(3)?
.read()?
|> expect_value(1),
).write(4)?
.overwrite(5)
.read()?
|> expect_value(3),
).read()?
|> expect_value(4),
).read()?
|> expect_value(5)
Bool.True
}
main! = |_| {
Ok({})
}
I would have expected something like this:
expect {
_result = CircularBuffer.create({ capacity: 3 })
.write(1)?
.write(2)?
.write(3)?
.read()?
|> expect_value(1)
.write(4)?
.overwrite(5)
.read()?
|> expect_value(3)
.read()?
|> expect_value(4)
|> read?
|> expect_value(5)
Bool.True
}
main! = |_| {
Ok({})
}
Should I file a separate issue for this?
Side-note: consider a |> f?. I guess this means (a |> f)?, or equivalently f(a)?. However, what if f is not a function, but it's a Try(_ -> _, _)? In this case, a |> f? should probably mean a |> (f?), or equivalently (f?)(a).
So perhaps a |> f? should always mean (f?)(a), and a |> f()? should always mean f(a)?, this way there's no ambiguity.Consequently, the formatter should not replace a |> f()? with a |> f?, it should leave the parentheses in this case to avoid ambiguity: a |> f()?.
Oh wait, there's still an ambiguity if f() returns a Try(_ -> _, _). I guess in this rare case the user should write a |> (f()?).
Edit: I'm over-complicating things. If f is a Try(_ -> _, _), then the user should just write a |> (f?) explicitly.
Should I file a separate issue for this?
@Aurélien Geron yeah please do!
Aurélien Geron said:
Side-note: consider
a |> f?. I guess this means(a |> f)?, or equivalentlyf(a)?. However, what iffis not a function, but it's aTry(_ -> _, _)? In this case,a |> f?should probably meana |> (f?), or equivalently(f?)(a).
that might be a case where () could be load-bearing, e.g.
a |> f()? is equivalent to (a |> f)?a |> f? is equivalent to a |> (f?)"load-bearing" spotted
Looking at all exercises, it seems that a |> f? always means (a |> f)?, never a |> (f?). Since a |> (f?) is arguably very rare, I would argue that users should be explicit if that's what they want, so a |> f? should always mean (a |> f)?. I think it's simpler to remember than "a |> f? means a |> (f?) but a |> f()? means (a |> f)?". Moreover, if f() returns a Try(_ -> _, _) then the user might expect a |> f()? to mean a |> (f()?).
hm but a |> f? really looks like it's a |> (f?) to me :sweat_smile:
I see your point, but doesn't a |> f()? also look like a |> (f()?).
Definitely not a hill I'd die on. :smile:
they both do :laughing:
Yeah, I guess that's the core issue: a |> f? looks like a |> (f?) but (a |> f)? is far more common/useful. Not sure how this can be fixed.
Hmm, maybe it’s time to hire a plumber to fix our pipes
I’ve heard there’s a way to prevent code smells, but it’s a trap!
Richard Feldman said:
they both do :laughing:
Does enforcing (either through error or formatter) that there are either parentheses or a space help? i.e only accepting a |> f ?, a |> f() ?, a |> (f?), and a |> (f()?).
Now that I look at it it's not a huge change...
I thought that ? with a space was the binary operator (map_err): isn't that going to cause issues?
I looked at some more code with |> and ? today and I'm actually growing fond of a |> f? to mean (a |> f)?. It's nice and short, and I read it like I read a sentence ending with a question mark: the ? naturally comes last.
Aurélien Geron said:
I read it like I read a sentence ending with a question mark
Learn from Spanish: ¿a |> f? vs a |> ¿f?
I was pretty happy with the previous -> syntax :p
We've also had these issues before:
I am a bit late to the party, but if the main motivations is to make a = $b->func() where func returrn (type of a, type of b) assing to $b. wouldn't it be possible to apply the syntax sugar based on how it is used, for example a, $b = $b->func() will return normally, but a = $b->func() will assign one of the result to $b. I think that could make sense. I like the a = $b->func() pattern a lot and it comes up all the time
Last updated: Aug 12 2026 at 12:35 UTC