There are several different traditions for this in real-world languages. Many of them have colonised previously-unused punctuation, rather than having a strong design ethos to them. The semantics also vary: some pass a value as the next argument to a function (or only to a curried application), some instead pass it as an inserted first argument to a function call, some have reverse or unpacking pipelines, some permit only single-parameter functions, and some only have stream processing.
Haskell has multiple in its standard library:
& has type a -> (a -> b) -> b, the classical left-to-right pipeline providing the value on the left as the next argument to the function on the right. This is not in the Prelude but can be imported from Data.Function; it is the most common realisation of this type in Hoogle, but there are others in other libraries. You could use this as something like range 1 10 & reverse & filter odd. $ has type (a -> b) -> a -> b, reverse pipeline right-to-left. This one is in the Prelude. You could use this as something like filter odd $ reverse $ range 1 10. $! is $ but strict in the value argument. - Versions of the operation are also provided for functors (
<&> and <$>), applicative functors (<**> and <*>), and other types, most obviously monadic bind >>=. . performs the function composition without the final application to a value.
The conventional pipeline operator in R is %>%. It is a library operator from the ubiquitous dplyr and magrittr packages, and it works a little differently to the others — it's in effect a macro rewrite that inserts the left-hand value as the first argument of the function call written on the right:
1:10 %>% rev %>% filt(odd)
would expect filt to be defined as function(vector, predicate). This is the opposite order to the Haskell version. Note also that the odd predicate needed to be parenthesised, to make filt(odd) a function call. There are many other third-party pipelining operators as well, and support in this one for inserting the value at a placeholder position.
Several languages include |> for pipelining, but with differing semantics:
- F# uses
|> as its main pipe operator, passing the value on the left as the next argument to the function on the right. You could use this as something like range 1 10 |> List.rev |> List.filter odd. It also uses ||> for a tuple-unpacking version and <| for the reverse order. >> and << perform unapplied composition. Elm uses these as well. - OCaml has
|> since version 4, but not the others from F#. - Hack uses
|> with explicit placeholders for the piped value on the right-hand side: range(1, 10) |> Vec\reverse($$) |> Vec\filter($$, $n ==> $n % 2 == 1) inserts the argument in place of $$. - Julia also uses
|>, and only for single-parameter functions, but its macro support allows libraries to extend this to provide placeholders for the argument value. - Elixir has
|> too, but working like in R rather than F#. - The TC39 proposal for JavaScript pipelines uses this operator with Hack semantics and
% as the placeholder token.
Concatenative languages use juxtaposition to represent this pipelining (and function composition in general). Mere adjacency is sufficient to pipeline the output of one function into the next already. This holds for stack-based Forth, Kitten, Joy, and others, and for inline ones like Om or Kihi.
Unix shell languages use | to compose stream pipelines, as do PowerShell object pipelines and many other inspired languages. These are connecting input and outputs streams, rather than nested function applications. jq, being quasi-concatenative, has both juxtaposition and | for pipeline steps, and they can be used in slightly different places; the input value is out of band and not a parameter regardless.
Raku uses the pretty verbose andthen, with some short-circuiting semantics, and elements of placeholders and method-call shorthands. This is borderline to constitute a pipeline.
Any language with uniform function call syntax provides pipelining via its method application operator, typically .. This typically provides the left-hand side as the first parameter of the function, but some approaches allow specifying the receiver in the declaration. This does require an increased level of multiple dispatch, but no new syntax at the call site. Extension methods, as in C#, give a more restricted version of the same thing. In either case, you'd expect to write something like range(1, 10).reverse().filter(odd) as in your example.
There is a wide variety of syntax in use and subtly or drastically different semantics between existing languages. There isn't any visible coupling between the syntactic form in use and the behavioural design of the operation, so fixing one doesn't point at a specific tradition for the other. Many cases are simply an available symbol, or a symbol borrowed from another language but given quite different meaning locally.
Alt + '+' + DEADBEEFor copy-pasting it every time $\endgroup$\forallitself in the language, if that's what the user types anyway? $\endgroup$filteris a very bad function name, languages seem to be contradictory about if this acts like afilterthat removes things or afilterto let them through. It's better to haveremoveorrejectorkeep, anything that is less ambiguous $\endgroup$