As far as I can tell, there’s only three primary ways to do staged function calls in a text-based language:
-
h(g(f(x)))
-
output_f = f(x)
output_g = g(output_f)
output_h = h(output_g)
-
f(x) |> g(&) |> h(&)
or better yetf(x)
|> g(&)
|> h(&)
(where I’m using &
as a pipe output placement operator).
I haven’t programmed in Erlang much yet (maybe will soon!), but I can only assume that without a pipe operator (1) and (2) are used all over the place. I think the pipe operator is the most clear when there’s a clear flow of data and ordering, and it keeps one from having to create intermediate bindings or having nested function calls. It can in fact also help cleanly organize new code in that you can do “pipeline-driven development”.
In both F# and Elixir, it’s pretty easy and actually quite common to mix and match all three options shown above. The point is that it’s nice to have all three at your disposal and to not be forced into one or the other when another is the better choice.
A nice example, I think, is an assembler I wrote in F# at one point:
let assembleFile (filePath: string) =
let directory = Path.GetDirectoryName(filePath)
let filename = Path.GetFileNameWithoutExtension(filePath)
let hackFilePath = Path.Combine(directory, filename + ".hack")
filePath
|> readLines
|> parseLines
|> buildSymbolTable
|> translate
|> writeAssemblyFile hackFilePath
There, (2) and (3) are mixed nicely.
The &
placeholder suggestion might lift that to the degree that I at least know what function is actually called, but I’d still have to look around elsewhere for what goes in the place of that nondescript &
, instead of having either a (hopefully aptly named) variable or function call right there, in place.
Do you really need to do that all the time when functions are named appropriately? The existence of a pipe operator in a language does not prevent you from creating a named binding for a function where the output is not clear.
“Make Erlang look more like Elixir” doesn’t count as a reason
That’s absolutely and definitively not the reason.
The reason is that pipe operators are useful as discussed above. F#, Elixir, Racket, Clojure, PowerShell etc. have them because they’re useful, not because of fashion. It’s just like pattern matching, once you use pattern matching or piping in a language, it’s hard to move to one that doesn’t have them.
It seems to me that it could be added to Erlang with little fanfare.