[erlang-questions] Pipe Operator in Erlang?
Fred Hebert
mononcqc@REDACTED
Thu Jul 9 19:44:55 CEST 2015
On 07/09, José Valim wrote:
>I agree with the general feelings of the thread. F# relies on currying.
>Elixir relies on macros (it is actually closer to the thread operator found
>in some lisps).
>
>Also F# has most of its standard library expecting the "subject" as last
>argument (due to currying). Elixir defaults to the first argument. Erlang,
>for better or worse, has them mixed (see binary and lists modules).
>
I agree with this. The only workable form I could imagine could go
something like this with macros, without major changes to the language:
pipe(Init, a(_, X), b(Y, _), c(_, _, 41.12)), where '_' gets replaced by
the threaded in 'Init' state. The obvious problem is lack of
composition:
pipe(Init, pipe(Init2, a(_))) where you can't know if a(_) refers to
Init or Init2 at a glance without knowing precise evaluation rules.
Generally I haven't felt I missed this feature too much in Erlang. In
the cases where it could really be nice, I resorted to using:
pipe(Init, Funs) ->
lists:foldl(fun(F, State) -> F(State) end, Init, Funs).
pipe(Init,
[fun(S) -> set(S, 230) end,
fun(S) -> update(S) end,
fun(S) -> output(S), S end]).
Obviously, one could easily come and swoop in with a macro:
pipe(Init, $$, % $$ is replaced by 'Init'
set($$, 230),
update($$),
fun() -> output($$), $$ end)
The problem is that composition is not obvious:
pipe(Init, $$,
pipe($$, $_,
set($_, 230),
update($_),
fun() -> output($$), $_ end))
In that case, the last 'output($$)' would try to print the literal '$$'
value instead of the substituted one given underneath it all, it's
equivalent to:
pipe(Init, [fun(S) -> pipe(S, [...]) end]).
Woops! But what's cool? Add in a maybe pipe!
maybe_pipe(Init, Funs) ->
%% use throws and catches if you wanna go faster
lists:foldl(fun(F, {ok, State}) -> F(State)
; (F, {error, Err}) -> {error, Err} end,
Init,
Funs).
And with the same set of parse transforms you can change:
f(X0) ->
case g(X0) of
{ok, X1} ->
case h(X1) of
{ok, X2} -> {ok, X2};
Err = {error, _} -> Err
end;
Err = {error, _} ->
Err
end.
Into:
f(X0) ->
maybe_pipe(X0, $$, g($$), h($$)).
Which translates to:
f(X0) ->
maybe_pipe(X0,
[fun(X) -> g(X) end,
fun(X) -> h(X) end]).
And all of this is doable today in library code for or from anyone,
doesn't fundamentally change the shape of Erlang, although it will
definitely be more confusing for newcomers or people not familiar with
the concept.
All it needs is someone angry enough to do it. I don't believe (from
doing experiments like Hubble[1]) that it's particularly hard.
Regards,
Fred.
[1]: https://github.com/ferd/hubble
More information about the erlang-questions
mailing list