[erlang-questions] Pipe Operator in Erlang?

Richard A. O'Keefe ok@REDACTED
Fri Jul 10 08:27:45 CEST 2015


On 10/07/2015, at 5:17 pm, Pierre Fenoll <pierrefenoll@REDACTED> wrote:
> A use case I often bump into is modifying some state using the accessors already written for this structure.
> 
> For example, modifying a record:
> 
>     PvtFuns = [ fun add_pvt_type/1
>                      , fun add_pvt_vsn/1
>                      , fun maybe_add_pvt_api_key/1
>                      , fun maybe_add_pvt_tree/1
>                      , fun add_pvt_enabled/1
>                      ],
>     NewRecord = lists:foldl(fun(F, C) -> F(C) end, Record, PvtFuns),

I'm aware of this style, but honestly,

    Pvt_Funs = add_pvt_enabled(
               maybe_add_pvt_tree(
               maybe_add_pvt_api_key(
               add_pvt_vsn(
               add_pvt_type(
                   Record)))))

is quite clear.  There is nothing superfluous in that,
unlike the folding version, and it copes neatly with
multi-argument functions as long as the "subject" is the
last argument.

Of course in Smalltalk, this would be

    Pvt_Funs := Record
        add_pvt_type
        add_pvt_vsn
        maybe_add_pvt_api_key
        maybe_add_pvt_tree
        add_pvt_enabled

because unary "functions" come after their argument in
Smalltalk.

Now suppose Erlang borrowed an idea from Pop-2 and said
that   e0.f[(e1,...,en)] -- with [] meaning optional --
meant the same as f(e0[,e1...,en]).  Except that . is
already taken, so let's try ¤ .

    Pvt_Funs = Record
        ¤add_pvt_type()
	¤add_pvt_vsn()
	¤maybe_add_pvt_api_key()
	¤maybe_add_pvt_tree()
	¤add_pvt_enabled()

What's the difference between this and the pipe operator?
(Aside from the fact that it's based on a much older programming
language?)  Answer: the pipe operator is SEMANTICS: E |> F
is a call to an actually existing "thing" called "|>".
> (|>);;
val it : ('a -> ('a -> 'b) -> 'b) = <fun:it@REDACTED>
But suffix function calls are SYNTAX.  In E¤F, F is not an
expression and is not evaluated by itself, and ¤ has no
meaning outside this construction.

> 
> Or some opaque structure, with accessors written more seriously:
> 
>     NewContext =
>         cb_context:setters(Context, [ {fun cb_context:set_doc/2, []}
>                                                      , {fun cb_context:set_resp_status/2, 'success'}
>                                                      , {fun cb_context:set_resp_data/2, []}
>                                                      , {fun cb_context:set_resp_etag/2, 'undefined'}
>                                                      ])

I'd rather see this as something like
    New_Context = cb_context:set(Context, [
        {doc,         []},
        {resp_status, success},
        {resp_data,   []},
        {resp_etag,   undefined}])

However, the "suffix function call" *syntax* (remember,
¤ is NOT AN OPERATOR) would turn this into

    New_Context = Context
	¤cb_context:set_doc([])
	¤cb_context:set_resp_status(success)
	¤cb_context:set_resp_data([])
	¤cb_context:set_resp_etag(undefined).

So if
	<primary> --> <primary> <call suffix>
	<call suffix> --> '¤' <opt mod> <fname> <opt args>
	<opt mod> --> <atom> ':' | <var> ':' | <empty>
	<fname> --> <atom> | <var>
	<opt args> --> <empty> | '(' ')' | '(' <expr list> ')'

will satisfy you, suffix function calls could be implemented as
a minor parser hack with no change to the AST and no implications
for runtime.  (Like I say, syntax, not semantics.)
> 
> Scoping rules for nested pipes looks like a hard problem, but I am not sure nestedness would be a great thing to have…

Suffix function calls have no scoping issues.

If anyone wants to continue this thread, please be careful
to distinguish between "the composition OPERATOR" and
"suffix function call SYNTAX".








More information about the erlang-questions mailing list