Algorithms + Data Structures = Programs - Episode 113: The C++26 Pipeline Operator with Barry Revzin!
Episode Date: January 20, 2023In this episode, Conor and Bryce talk to Barry Revzin about the pipeline operator |>, C++ Ranges and more!Link to Episode 113 on WebsiteTwitterADSP: The PodcastConor HoekstraBryce Adelstein Lelbach...About the GuestBarry Revzin is a senior C++ developer at Jump Trading in Chicago, a research and technology driven trading firm. After programming for many years, he got really into the nuances and intricacies of C++ by being unreasonably active on StackOverflow (where he is the top contributor in C++14, C++17, and C++20). A lot of his C++ knowledge comes from just answering questions that he doesn’t know the answers to, especially when he answers them incorrectly at first.His C++ involvement escalated when he started attending standards committee meetings in 2016, having written dozens of papers for C++20 and now C++23. You might know him from such features as <=>, pack expansion in lambda init-capture, explicit(bool), conditionally trivial special member functions and, recently approved for C++23, deducing this.Outside of the C++ world, Barry is an obsessive swimming fan. He writes fun data articles for SwimSwam and also does analysis for the DC Trident, a professional swim team featuring Olympic Gold Medalists Zach Apple and Anna Hopkin, managed by two-time Olympian Kaitlin Sandeno.Show NotesDate Recorded: 2023-01-15Date Released: 2023-01-20Iterators and Ranges: Comparing C++ to D to Rust - Barry Revzin - [CppNow 2021]Keynote: Iterators and Ranges: Comparing C++ to D, Rust, and Others - Barry Revzin - CPPP 2021Kona Photo of Barry and Michael SwimmingCppCast Episode 237: Packs and PipelinesP2011 A pipeline-rewrite operatorP2672 Exploring the Design Space for a Pipeline OperatorC++20/23 Ranges LibaryRanges-v3 LibraryBoost.Lambda LibraryBoost.Lambda2 LibraryTC39 Pipe Operator (|>) for JavaScriptIntro Song InfoMiss You by Sarah Jansen https://soundcloud.com/sarahjansenmusicCreative Commons — Attribution 3.0 Unported — CC BY 3.0Free Download / Stream: http://bit.ly/l-miss-youMusic promoted by Audio Library https://youtu.be/iYYxnasvfx8
Transcript
Discussion (0)
How do you write that as a lambda?
Well, it's like open bracket, close bracket, open paren, auto, ref, space, it, close paren,
arrow, decl type auto, open brace, return, start it, semicolon, close brace.
So it's something like 30 characters to three is what the difference is. Welcome to ADSB, the podcast, episode 113, recorded on January 15th, 2023.
My name is Connor, and today with my co-host, Bryce, we interview Barry Revzin about the
potential C++26 pipeline operator, C++ ranges, and more.
But our guest today is Barry Rebsin.
I'll do the introduction.
Barry is an international keynote speaker.
Probably, actually, Bryce, I'm not sure if you know anyone that went faster from giving
his first talk to international keynote speaker than Barry,
because he gave one talk. And then he gave one talk at C++ now. And then boom, that was it.
Next talk, international keynote speaker. That's pretty impressive. It's amazing talk.
It compares, I actually don't know the title of it. It compares D, C++, and Rust iterator models.
Fantastic talk.
I'll put both the C++ now.
And I think it was CPPP was the conference that you keynoted at?
Yeah, that's right.
So there's that.
He probably has my favorite C++ blog, although it's not just about C++.
But out of all the blogs out there, his is my favorite.
So definitely go check out that.
And what else can we say? You're one of the most prolific paper writers in contributing to the C++ language and library evolution. I don't know if anybody's done the numbers on it,
but for C++23, you probably wrote more papers than anybody else. I don't really count. Yeah,
it's probably,
uh,
I'm well,
yeah,
we'll put it out.
We'll throw it out there to the listeners.
I'm sure there's someone keeping,
keeping stats.
Let us know where Barry ranks.
If he's not number one,
he's probably in the top couple because there's a ton of papers that you've
written,
not just for the C plus plus 23 cycle,
but going back.
Yeah.
And I'm not sure what else should we say? Should we let you
introduce the things that we haven't said? I'm not sure if you want to say where you work.
Some people do, some people don't. Sure. So I work at Jump Trading in Chicago,
which is a trading firm that does a little bit of everything. We trade everything,
all asset classes,
all time zones, all products. Very cool place to be. I've been there for eight and a half years.
It's a pretty nice place. Outside of the C++ world, I guess I'm very big into swimming,
which is something that a lot of people don't know about me. and it is something that if you see my tweets they're
kind of divided into two categories of like random c++ things and whatever's going on in the swimming
world um briefly there was this uh professional swim league called the isl i'm hoping it comes
back but we'll see if i not um and i was involved in that which is which is a ton of fun um for for
a fan yeah do you run the swim slam i know i've seen swim slam is that something separate
that uh i think if you go if you youtube your name you'll get your two c++ talks and then you'll get
some swimming related things uh yeah so sometimes it's like the premier swimming media organization
um if you if you want to know swimming news that's definitely like the
place you want to go um it started i don't know maybe 2012 2011 somewhere around there um and i i
contribute to it periodically i write like random data articles i run numbers for them or a bunch of
stuff uh definitely definitely didn't found it or own it or anything like that i just i just
contribute periodically okay just a platform that you contribute to. And also too. So were you,
uh, did you, I think you told me this, you swim, you used to swim. Um, but now you don't swim as
much and you run or do you do both now? Or are you primarily a runner? Cause I'm not sure if
you've listened to the most recent episode, but Bryce is thinking about becoming a runner.
And I know that you at least have been going on runs cause I've seen you tweet stuff about it
over the past year or so.
Yeah, definitely.
Definitely run and swim.
I'm much better at swimming than running.
But it's obviously a lot easier to run because you just like put on a pair of shoes and just go.
You don't need to like plan pool time and get there.
Actually, that was one of the really nice things about the Kona meeting is that it's just on the ocean.
So I don't have to plan to go swim. I just walk out
into the ocean and go and I'm already there, which is super nice.
During that meeting every day at lunch I would just go swim along with Michael Park
and Matush. It was super nice. I could see why people like Hawaii.
Oh yeah. I don't recall where
I saw a photo. It might have been on twitter but there
was a photo of you michael park and was it matush as well like there's a few of you
also i think you were in the water uh and i was like damn that looks quite nice it was it was
awesome because it's it's like swimming except you're in the ocean and there's you know fish
um on one of the days i nearly ran into a sea turtle uh which is yeah i'm just like i'm kind of you know i'm doing my thing and like there's like i i see this blob out of the days, I nearly ran into a sea turtle. What? Yeah, I'm just like, I'm kind of, you know, I'm doing my thing.
And like, there's like, I see this blob out of the corner of my eye.
And I was like, whoa, what was that?
And it's like a turtle.
So I'm just like start following around for a few minutes.
Wow.
That's awesome.
I thought usually the sea turtles are pretty, pretty timid.
Like they don't get too close.
No, they're actually, they're pretty like, they don't, they don't really give a about you. They really,
yeah. Like if you're at some of the beaches in, uh, in Kona, um, and like, they think that there's
some food like near and area where they are, they'll just come on up. Really? I thought,
I guess my impression of that is that I've only been to Hawaii once, but when I did go, I went, what do you call it, snorkeling?
And they told us to like give the turtles a lot of space.
Yeah, so, and it's actually state law that you're supposed to give them like a good, you know,
I don't know, 20 feet or so.
You're supposed to sort of stay away from them.
I don't know why that is.
I don't know if it's that like it's really easy for humans to to harm them but i i think it's that like
they don't want them to like get too used to um uh like being around humans and i think that they
also can like give you a pretty nasty bite but i do know that it's like a it's like a state law
well we got to cut this out then because clearly Barry was breaking state laws.
You're lucky you didn't get arrested.
I did not touch it.
I don't think it really counts if you're just like swimming around and like the sea turtle just like –
Listen, officer.
He approached me.
I just don't think – I don't think that's – I don't think Barry's going to get in trouble for that. Well, we apologize if the C++ community loses a luminary figure, an international keynote speaker, because we—
Barry can still write papers.
And, you know, the jails in Hawaii, not to offend the great state of Hawaii, but, you know, the airports in Hawaii are, like, all outside.
So I'm sure the jails in Hawaii probably have like a nice pool.
All right.
Well, stay tuned, folks.
We'll give you live updates as to Barry's incarceration status.
Not where I thought this podcast was going.
Yeah.
Well, you know.
Yeah.
Nobody ever knows what this podcast is.
Chaos with sprinkles of information.
That's our tagline that someone on Reddit gave us.
Well, speaking of chaos, I mean, we can – I'm not sure if you had plans of what you want to talk
about, Barry.
Definitely at the top of my list is your pipeline operator papers, because it's probably for
C++26 the number one thing that I'm excited for, given that we have now a bunch of ranges,
adapters in C++23, specifically a bunch that
don't work with the pipe operator, which we can talk about in a sec. But before we launch into
that paper, and then also the error propagation paper, because I want to talk about that one as
well. Is there anything you want to, because you've written so many papers, obviously active
in C++, anything you want to start off or things you want to say. Oh, we should also mention you were a guest on CppCast, which is back, but you were a
guest back before it took a hiatus.
We'll put that link in the show notes where I think you talk about, I think pipes is in
the title of that episode.
Yeah, I was talking about, yeah, I remember talking about pipeline there too.
Yeah.
Yeah.
I mean, we can talk about whatever.
I have no particular agenda.
All righty.
So, I mean, Bryce, you can go Google this paper. Have you read the paper,
Bryce? Probably the pipeline. Well, so there's two papers and we'll get Barry to give us a little
background both, but there's 2011, which was the first paper. And I believe that was published in
2020. I'm going to say, but that could be wrong about that. But there's more recently a paper,
what's the number? 2672. Looks like there's only R0 at the moment. And that came out in October of 2022. That paper is entitled Exploring the Design Space for a Pipeline Operator,
which it starts off by sort of referencing the... Yeah, I've only read the first one.
Yeah, it starts off referencing 2011, but then talks about the fact that there's a number of
different models. And anyways, I'll throw it over to you, Barry. You can give us an overview of the it starts off referencing 2011, but then talks about the fact that there's a number of different
models. And anyways, I'll throw it over to you, Barry, you can give us an overview of the paper
and in however much detail you want. And then we can talk about, is it going to show up in C++26?
Are people for it, against it, et cetera, over to you. Yeah. So, so that second one is, I think the right way to describe that second paper is abusively long, which is how Zach Lane described it. And I ranges works is it has certain kinds of algorithms are things that you can pipe into.
So instead of writing f of x, you can write x pipe f.
And those algorithms are all the range adapters.
So you can have some range pipe into views transform, piped into views filter, piped into views join um and that gives you this nice linear flow um of of the work that's being
done um which which makes it like much nicer to to read um since a lot of these things once you get
to all a lot of the adapters take a second argument in addition to the range and trying to
write that um as just function calls tends to be very tedious because you have to read it all inside out,
which is, you know, I find it a bit hard to read.
But that syntax is really nice, but it has two problems.
One is that it requires some library machinery to implement,
and that library machinery doesn't lend itself very well to providing good diagnostics.
So if you write something like
use transform r comma f and f isn't a valid function for that range, you're much likely to
get an error that says something like that than if you do r piped into transform of f.
And this is because in the, in the first case,
you're actually trying to call the thing.
And in the second case,
you're trying to call some pipe operator that ends up being removed from
consideration.
So you'll tend to get errors for like other pipe operators that aren't
relevant.
So that's not the best.
The other problem is that there's a bunch of other algorithms that you can't
pipe them to.
So like all the,
all the things that like are typically referred to as algorithms,
like all this stuff in std ranges.
Cause typically what you do is you build up your pipeline and then you
consume it somehow,
right?
You,
you either just iterate over it with a for loop or you pass it into like,
now we're going to have ranges two and C++ 23 to like turn it into a
collection.
Or you,
you just use something like,
I don't know, ranges min or ranges or you you just use something like i don't know ranges
min or ranges max or ranges fold or something like you you consume it with some kind of algorithm
um but you can't pipe into those algorithms because we just decided that that wasn't that
wasn't a thing so you you kind of go back to this inside out style so you have to write like
ranges min and then your pipeline instead of just doing R pipe to transform pipe to min, which is something that you can write in other languages.
Wasn't it quite intentional that we made things like ranges fold, like the algorithms not pipeable because they weren't lazy?
So, yeah, it's intentional that the adapters and then so in range c3 also has
actions which which aren't lazy they are they are eager as well those are pipeable um but but not
the algorithms i don't i don't really think of that distinction as especially meaningful um
piping i think of as then why didn't we make the uh the algorithms well part of it is just like
that's that's just like more work that you have to do. There's also this issue of not everything can be easily made pipeable
because you don't always have this clear way of determining whether your call is a full function
call or a partial function call. So some of the easy examples with range adapters are things like
Zip and Concat, where you can't differentiate between
which kind of call you're making because well they couldn't just take an arbitrary amount of
ranges um so zip zip a is a is a valid thing and you you have no way of knowing at the call side
of which which uh that you meant it to be like a partial invocation this is also a problem with
things from like the sender's proposal like things like when all anything that's like very addict like that, it, you can't really make
it payable. Yeah. So that's something I noticed when I, I'm not sure if you saw that tweet,
but I tweeted a couple of weeks ago that I realized GCC trunk, AKA what will be 13 when
they release it has implemented all the range adapters from C++23. And while I was, if you
actually look at the code example, it's zip transform, zip adjacent, I think as well.
And then the pairwise specializations, none of those are, at least in the GCC implementation,
and I didn't go take a look at the standard pipeable. Yeah, they're not, they're not,
they're not even specified. That's how they're designed. So the transforms have this other problem in that the first argument is the function.
So if they're pipable, that's not really like the model of piping because we pipe the first argument and you'd want to pipe the second.
So this is basically the genesis of this paper is that we have this one problem that getting the piping to work requires library machinery that's just like tedious to implement at best and isn't great for diagnostics at worst and then there's all this
other stuff that we want to make pipeable but doing so is possible but but just tedious um and
with like we could do it for the standard library algorithms but that doesn't help user algorithms
that they might want to write so this is like just like more tedious for everyone. So it would be
nice to have a way of
getting the syntactic
benefits of this without all
of the complexity and all the machinery.
So this is where the
pipeline idea was born.
Well, I should say
born. We didn't invent
this concept. This concept exists in other
languages before us. And I say us because colby pike um better known as vector pool um
is is the first person that i saw pushing this idea so he's he's my co-author on that initial
paper um and so the first revision of the paper the first thing that we tried to do was just define the simplest model,
which is that X pipeline F of Y evaluates as F of X comma Y. We take the argument on the left-hand
side and a call expression on the right-hand side, and that is evaluated as the left-hand
argument as the first argument. It gets inserted into the first slide of the call. So there's no intermediate expression.
F of Y is never evaluated.
It doesn't even have to be valid.
It's just a single thing.
So this is a fairly simple thing to explain.
It behaves a lot like member function call,
if you think about it.
Like x dot f of y, what that does is it shifts x over
to be the first argument.
And especially now with deducing this,
it really does behave like that.
Like we're moving this parameter around
and making one function call.
So that was kind of the initial design that we had.
And this was the 2011?
Yeah, so that was the R0.
And so it turns out, as with a lot of things that are seemingly simple, there's a lot of interesting things to think about in this space. One ends up being, well, what's the precedence of this thing? And it kind of turns into like a surprisingly weird thing to consider. So one example might be, so if, okay, so x pipeline to f of y validates as
f of x comma y, what does like f pipeline to f open paren y close paren open paren close paren
mean? Which of the two function calls are we moving x into? And the same kind of holds for
other kind of like post-script expressions. So you end up kind of wanting to have it to have
very high precedence because it behaves a lot like dot.
So you want it to behave like dot.
And there are a lot of kind of interesting things there.
But then we kind of started talking about, well, like there's a lot of other interesting things you might want to do with piping.
So the zip transform example you brought up, you don't want to pipe into the first argument.
You want to pipe into the second, maybe even the third, depending on what you want to do. So wouldn't it give more flexibility if we
open up to the user where we're moving this argument into? So like if there's a placeholder
that we could pipe into. Well, there's also the case where you might want to
pipe it into multiple arguments, right? And that also becomes useful if you,
when you want to do like more complex things,
like, you know, fork join style models,
which you can sort of emulate with some sort of like tuple-like object where,
you know, you do something like similar to,
similar to like a zip or like a win-all in senders.
But then like later in the chain,
you want to decompose it into its constituent parts.
And maybe you want to call a function
where you want to say like,
call this with the first and third argument of this tuple
as the two arguments of this function.
But without a model that has a placeholder,
it's a little tricky to do that cleanly.
Yeah, so placeholders are also interesting
because arguably they make it more clear what's going on.
Like if you see this expression on the right-hand side
and you see a placeholder, you kind of know that like,
okay, well, this is where the argument on the left is going.
But then once you get into placeholders,
so one of the interesting issues with multiple placeholders is
like, okay, well, if the left-hand side is an L value, then having multiple placeholders, okay,
well, that's obvious, right? You evaluate the left-hand side once and you just like use it in
both places. But if the left-hand side is an R value, well, like what do you actually do with it?
You definitely don't want to evaluate it twice because that just seems totally, totally wrong
and very surprising. But if you evaluate it once, that just seems totally totally wrong and very surprising
um but if you evaluate it once do you like give it as an r value in both places do you like
escalate it to be an l value um that's that's kind of hard like i don't know what the right
answer for that is so like i think in the in the in the last paper i'm um i suggest escalating it
to be an l value like evaluate it once and then treat it as an L value multiple times.
Alternatively, make it ill-formed if it's not an L value. Because you can always
stick in a function in the middle to turn it into an L value and then pipe that L value twice if
that's what you really want. But at least then it's explicit. So this is kind of what I mean
by like once you start diving into things, there's all these like weird issues that come up. So one
is like, okay, like where can you pipe things into? Because one of the things, when you look
at it syntactically, you have this expression and then you have pipeline and then you have stuff.
So like it really looks like you're always evaluating the left-hand side, right? So you
don't necessarily want to allow piping into a location
that it wouldn't necessarily be evaluated in. So for instance, like x piped to like y or placeholder,
well, you're not always evaluating that expression then. So like if you unconditionally evaluate it
but then use it in a context that it has short-circuiting, that's kind of weird.
And then what if you then use it in an unevaluated context on the right-hand side?
You have an expression, then you pipe it into a decl type of that expression.
Also, well, it looks like you're evaluating it, but you're not if we think about the expression
rewrite.
So is that something you want to allow or no?
So this is why the paper ends up being abusively long uh because
you have to deal with all this stuff um where it gets even more interesting is that like once you
have placeholders you can think about like well what does the we have this right hand expression
that has placeholders in it but like what is how does it behave like if you look at that thing
in like if you look at like f open paren let let's say dollars or placeholder, like f open paren, dollar close paren
that's kind of like a
lambda
right?
you can kind of think of it as like
constructing a lambda with placeholders on the fly
and then invoking it
it's not exactly that
and in fact in the old
library only boost lambda model
that's how you would write a lambda function is
you would have these um these like underscore one underscore two underscore three uh placeholders
that were these you know magic objects and you could do like underscore one plus three and then
that would give you back a lambda that would do take one argument and then that would add three
to it and i i still know some people who use that library,
even though we have, you know,
C++11 lambdas because it's such a,
when you're doing something that the library,
when you fit into one of the use cases,
like, you know, adding up some things
that the library is really good at,
like it's much more concise
to just write your Lambda that way.
Yeah, absolutely.
And there's actually,
there's a new boost library called boost Lambda too,
that Peter Demoff wrote a couple of years ago.
And it's, I mean, it's, it's fantastic.
So when I was implementing zip as part of like,
so Tim Song wrote the paper for zip and then I implemented it kind of from
spec to make sure that all the wording was right.
And I use boost Lambda too for all the implementation because a lot of the stuff, so you think about
like, so zip iterator is like a tuple of iterators.
And every tuple operator, every like iterator operation is an operation on like through
this tuple, right?
So it's either like a tuple transform or like a tuple for each or something.
And all the functions that you want to do are like trivial so like dereferencing zip iterator is like dereferencing all the underlying iterators
of of the stuff into to a tuple transform so with boost lambda 2 how do you how do you write the
function that is dereferencing the first argument well it's star underscore one yeah uh how do you
write that as a lambda well it's like open
bracket close bracket open paren auto ref space it close paren arrow decal type auto open brace
return star it semicolon close brace so it's like it's something like 30 characters to three
is is what the difference is yeah um this kind of placeholder stuff,
if we can have it in Lambdas directly,
would be super useful.
So once you start dealing with placeholders in pipeline,
well, that lends itself to like,
well, what if you use that same technology
to actually construct a Lambda directly?
Like what if we had a new kind of Lambdas
that was just based on placeholders?
And so there's a whole section in the paper
of what kind of exploring what that means, like what kind of syntax could we use and how do we like
differentiate it from the languages that we have today and how do those apply?
So if we had that new sort of syntax, would you then, instead of having placeholders directly
in the piping operator,
would it just be that you just say that the right-hand side is a function object
and then just the convention would be that people would use the super-abbreviated lambda syntax to write them?
Or would you still want the placeholders directly in the piping syntax?
I think you'd still want the placeholders directly because there's a few significant differences
in why you wouldn't want it to just be a function call.
One is, for lambdas, we'd still need capture
because even with the placeholder case,
we're still C++ and we don't want to do either.
You wouldn't want to pick an implicit capture,
so you'd want to make it explicit for one reason or another. But then it's
not really a function call because if we know we're piping, we know we're evaluating this directly.
So we don't want to introduce another scope, which is particularly important because one of the
things, once you get into placeholder land, you can put an arbitrary expression on the right-hand
side of pipe. We just give pipeline extremely low precedence,
and then you could do things like, you know,
maybe the motivating use case is doing like F of placeholder,
but you can also do F plus placeholder, F times placeholder, right?
You can use placeholder as like a substitute for parentheses.
But importantly, one of the things you can do is co-weight placeholder,
co-yield placeholder.
And so like if you want to do
the coroutine operations, then you can't
make it a function
because you can't introduce that other
function scope because then you break
coroutineness.
Yeah, that's a good point.
Which is actually one of the places where this whole
what if we think about it as a language bind
kind of model kind of breaks down because if you want to
support co-weight, which I think makes perfect sense to support, that doesn't really work by thinking about it as a function.
So it's like, I don't know, it's kind of like a weird thing.
One of the things to ask you, and actually you might not know the answer to this question.
I think you and I, well, at the end of the paper, you sort of articulate between the design choices, what you're, what you are in favor of and pipeline plus placeholder
is definitely something you want, which is definitely something I want as well.
Not just for the fact that there are a number of range adapters that I mentioned before
that don't work with the pipe operator and the fact that they don't work with the algorithms
in those, you know,
std colon colon ranges namespace.
It also enables you to do the duplicate things,
which is a whole other thing that I like,
because you can technically mimic some of the composition patterns
that you get from combinatory logic, which is like a big thing that I really like.
However, there are some people that are against the placeholder.
And I think one time i i tweeted
something and then gasper replied gasper asman who is a member of the c++ um committee and he
said please no don't put the placeholder uh in and i think i asked why but i don't think he replied
do you know what like the main sort of drawbacks other than, because I know the paper mentions
that it's a bit more verbose in the case where you have to, you know, you have a bunch of
unary things and yes, having to spell out the unary thing is a little bit uglier.
But in my opinion, that's like a weak argument that you're getting something less verbose
compared to like giving up the flexibility that you get with the placeholder.
Do you know what potentially Gasper and other folks that are against the placeholder, what their motivation for that is?
So I think the verbosity is a significant argument against what I hear from people.
And it's even something that I've seen multiple people suggest like, okay, well, placeholder, but allow the placeholder to
be optional so that you can pipe to something like f paren paren and have that work as if you
had the placeholder. If there's no placeholder, then it has to be a function call and then you
stick it into the first argument kind of thing. There are even people that argue against the need
for the paren. When we were standardizing ranges two there was a
um a point of contention about whether you would need to write like x pipe ranges two
with empty parens or not um you know so i i i think that there's there is a great desire for
conciseness from certain set of folks.
And just to clear up the ambiguity for the listener, this is ranges colon colon T-O, not like a second version of ranges that we're introducing.
Yes, yes.
It's hard to speak above that one function.
Or maybe a simpler way to explain that is like there are some people that if you have like X pipe F empty parens, some would argue that it'd be more elegant
to have just X pipe F.
It's definitely more elegant,
but like, you know,
the language that Barry just spelled out,
our lambda,
is like, you know.
Yeah, we're not an elegant language in many ways.
So yeah, so the two,
I think there are actually slightly different arguments.
The argument for two, I think they're actually slightly different arguments. So the argument for two,
I think, is a little bit different
in that if you look at
all of our unary adapters,
you don't have to put parens
for any of them, right?
So if you pipe to views reverse
or views join,
you can put the parens,
but you don't have to.
They work without it.
And in fact, I'd never see anyone write parens.
And so that works.
And so in that sense, if you think about, like, 2TO, like, stood vector, close bracket, that is unary, right?
Like, I'm just piping a range and I'm converting it directly to a vector.
So why is that the one unary adapter that I have to put parens in?
It can take other arguments, but I think the overwhelmingly common use case will be that it won't take any. is that we're not even sure if the core language blesses the implementation
and approach to getting rid of the parens.
Like if that is actually valid per the language.
So it's not even like this is like a structural design reason why the parens
are super important.
We're just like not sure if it's actually legit.
But the other, for Pipeline specifically,
so this is what I find interesting,
is that there's a JavaScript proposal for the Pipeline operator.
And I link to it in all the papers.
And there's been a lot of discussion on there over the years
about various designs.
And they've gone through the same kinds of,
well, there's three or four different models for Pipeline, which is the right one. about various designs and they're they've going through like the same kinds of like well like
there's like three or four different models for pipeline which is the right one and they've also
um seriously considered this like um use a placeholder but like make it optional and what
does it mean for not having a placeholder um and one of the issues that like the optionality does
add complexity to the design right because you have to determine what that means.
And in particular, like if you want x pipe f to automatically mean f of x, but you also
want x pipe to f open paren, like close paren to mean f of x.
How do you like, so does no placeholder mean you call the right hand side or does no placeholder
mean you insert into the right hand side?
Do we have different rules for f versus F, paren, paren?
This adds complexity.
There's also discussion there about,
and some of it was more specific to JavaScript,
like we wouldn't have this problem.
But how do you know that the user intended to do this or not
becomes more of an issue?
And the way that they're leading is mandatory placeholder.
Because placeholder itself, once you have placeholder at all, that's complexity, right?
Because the simplest version that was like the R0 version that we proposed, where the
right-hand side has to be call expression, and then you just stick it into the left-hand
side, that's a fairly simple thing to explain explain and it's a fairly simple thing to implement um
and i can even i can say that because like i implemented this in clang and it did not take
me very long um and like it was probably not the right way to implement it in clang like i didn't
like create a new ast node i just kind of hacked it into like the regular function call node um
but it it's like a very simple thing to implement versus placeholder is much more
complicated. And you have to deal with like, well, what things can you place hold into and not? And
can you use it more than one time? And like, what about lambdas? But it lets you do so many more
things and it adds a lot more explicitness to it. And so I'm not sure in the grand scheme of things that saving one to three characters per expression ends up being a make or break kind of thing.
Yeah, I kind of, I think I agree with you.
And I think that there's a, if we're going to do a language, if we're going to add new language support for a pipeline operator, surely one of the rationales and motivations for that is to enable pipelining of things that we can't do today. And I think a lot of those cases,
a lot of those more interesting cases,
like ones that Connor and I have talked about
in this podcast before,
would really benefit from having the placeholders.
So if we're going to go through the trouble
of adding language support,
I think we would lose a ton of utility
if we don't have placeholders.
And a world with optional placeholders would mean, you know, you essentially have two syntaxes,
and there would be a lot of potential ambiguities, and there'd be more things for people to have to learn.
And I don't mean, like, language ambiguities.
I mean, just, like, you look at the code, and you're like, huh, what does this mean?
Like, mental ambiguities. I mean, just like you look at the code and you're like, huh, what does this mean? Like mental ambiguities.
And I don't think that the additional, you know, a couple of characters you have to type is that bad.
And I also think, honestly, that it'll be a lot more teachable.
You know, when you explain like the pipe piping to people today, you usually tell
them it's like Unix, you know, like shells, it sort of works like this. And that's like a good
analogy, but that sort of assumes that the way that like a Unix shell pipeline works is like
this intuitive thing. And it is an intuitive thing because we all know it, but I think it's not
necessarily natural.
If you just look at some of these examples in the paper with the placeholder syntax,
I think if you showed somebody who had knowledge of C++
but had no knowledge of the pipe operator
and no knowledge of ranges piping
and no knowledge of unique shells,
I think that they would look at that and they'd probably understand what it meant.
Yeah.
All right, chip it. C++26.
So what do you think the odds are, Barry?
I don't know.
You don't put bets on your own papers?
I don't.
Well, also, because I don't even know what the bets on your own papers i don't um well like also because like i
don't even know what the right answer to any of these questions is so uh i mean i don't i know
what some of the wrong answers are like there's a pipeline model where you just where the right
hand side just invokes you treat the right hand side as a function and you invoke with the left
hand side which is like the f sharp model um that's definitely the wrong model for c++ because
that doesn't help us at all it just like makes everything more verbose. So I know some wrong answers, but I don't know what the right approach is.
That is the inverted invocation in the paper.
So that works very well for languages like F sharp that have a lot of other language support for
currying and partial function application. We don't have any of those things. And without those
things, that model just
doesn't make any sense. Yeah. That's something that I think about when you're talking about
the elegance or the lack thereof in C++ is that for languages that function invocation typically
requires parentheses, you end up in a completely different space than when you're
you know comparing it to a language like haskell or f sharp because those languages one are
functional at their core and two the it's just completely different like the way that you do
uh you know quote unquote piping in haskell like it's completely designed around the fact that
those languages you know have like you said partial application and currying, and you can do like things super nicely, like flip,
and it's just, you know, flip and insert that in front of a function. And you've, you've swapped
the order that you pass arguments in order to do that in C++, you know, you're calling a template
metaprogramming function and boost on a, that, you know, does the same thing, but it's, it's
nowhere as easy as just putting a function in front of your other function. And, you know,
it's just a different, you know, there's a part of me that's like, oh yes, I want this beautiful,
elegant model, but like, we're not Haskell. We're not F sharp. We can't get that. And yeah,
you have to, at least for me, I put my elegance like heart on the shelf and say,
we're in C++ land. Let's just get the flexible thing and sort of live with it.
So I think there's another potentially big advantage to language support for a pipeline
operator that we've maybe not talked about before, which is ranges piping is cool and
all, but there's a lot of library machinery uh you need to do to make it work and um uh there's this sender's proposal which is a proposal
for you know this asynchronous programming framework for c++ which also has used the you
know piping uh syntax um and also has a lot of language, a lot of library machinery to support that.
And I think that having this in the language would maybe not drastically reduce the inherent
complexity of these big library proposals, but I think it would decrease the complexity
somewhat.
And that I think is a worthy thing to do. Yeah. Yeah. I would say, I guess, as far as
like odds go, it really depends on, cause there, there are people that think that placeholders are
obviously the correct design and, and the design without placeholders is totally unpalatable.
And then there are people that think that placeholders are bad because they add complexity and they add verbosity to what will be the overwhelmingly common use case where you want to pipe into a function call and you want to pipe into the first parameter.
And so I think it largely depends on what the relative sizes of those two groups end up being.
And just to some degree, the existing piping that we have in ranges, it's not like we're
just going to get rid of that.
So the people who don't want placeholders or who want placeholders to be optional, then
that syntax, at least for ranges,
is going to be there forever.
Yeah, we can't really get rid of it.
Yeah.
All right, well, we're, I think,
roughly 45 minutes into this,
and that's about the halfway mark.
I have no idea how I'm going to cut this up, but before we move on to the next paper,
and maybe we'll turn this into like a mini Rust episode because we're
in our Rust phase of our podcast. We've temporarily turned into a Rust podcast, kind of. Stay tuned
to hear Barry's thoughts on the Rust programming language in part two of this two part interview.
Thanks for listening. We hope you enjoyed and have a great day.