[erlang-questions] Service platform (longish rant)

Vlad Dumitrescu <>
Mon May 7 22:32:05 CEST 2007


Hello everyone!

Let's see if I can express some ideas in an understandable way:

Chapter 1
--------------
There have been discussions about how to implement in Erlang something
like Haskell's monads. For example
http://www.erlang.org/ml-archive/erlang-questions/200503/msg00029.html
or http://www.erlang.org/ml-archive/erlang-questions/200302/msg00499.html.

Now, monads are rather abstract things that don't really feel at home
in Erlang (at least not in my eyes), not even if they were called
"warm fuzzy things" instead :-) What I think is interesting in the
first discussion thread mentioned, is the problem of chaining together
function calls in a way that is easy to make changes, without using
temporary variables whose names may create confusion or errors.

Ulf Wiger had in the first message above implemented a solution in a
nice way, but he admits later that he tried to use macros to get rid
of writing all the "fun()->" wrappers, but gave up.

The example was

with(Dict, Actions) ->
    lists:foldl(fun(F,D) ->
                        F(D)
                end, Dict, Actions).

    Dict = with(Dict0,
                [fun(D) -> read_options_file(D) end,
                 fun(D) -> store_options(Options, D) end,
                 fun(D) -> post_process_options(D) end,
                 fun(D) ->
                         {Path, [App, Vsn]} = get_app(D),
                         store_options([{app_name, list_to_atom(App)},
                                        {app_vsn, Vsn}], D)
                 end,
    ...................

I agree, not very friendly, but it works.

Chapter 2
--------------
One of the keys of Unix' success is its toolkit of utilities whose
functionality can be composed through the use of pipes and
redirection. These utilities can also be seen as services provided by
the platform.

Erlang processes are also service providers. Even function calls can
be regarded as such. But compared to the Unix ones, they are not as
easily composeable.

Compare
    ls | grep "*.erl" | sort | head -n 1
with
    hd(sort(grep(ls(), "*.erl")))

In the first case, the dataflow is clear, left to right. In the other
it is reversed and slightly encumbered by helper arguments. It is
easier to see what is going on if writing
    Files = grep(ls(), "*.erl),
    hd(sort(Files))
but this is much more verbose.

If message sending and receiving would be involved, then we either
have to wrap them with function calls, accept an even more verbose and
difficult to compose syntax or use Joe's proposed !! operator.

I like the latter idea best, then the example would look like
(assuming all operations above are implemented as processes)
    Files = grep !! {ls(), "*.erl"},
    hd(sort !! Files)
or on a single line,
    hd(sort !! (grep !! {ls(), "*.erl"}))

A bit disturbing, in my opinion.

The with/2 function from Chapter 1 would work nicely to solve this
issue, if reimplemented as

with(Dict, Actions) ->
    lists:foldl(fun(F,D) when is_function(F) ->
                          F(D);
                       (F, D) ->
                          F !! D
                end, Dict, Actions).


Grand finale
-----------------
I would like to suggest a syntax addition that would allow an even
nicer looking source code (if I may say that myself :).

    ls() => grep(_£_, "*.erl")   %% _£_ is a reserved variable,
                                         %%     meaning "the current
intermediate value"
          => sort                     %% this is a process name, could be a pid
          => hd/1

The operator would be semantically equivalent to

    with(ls(),
        [fun(X) -> grep(X, "*.erl") end,
         sort,
         fun hd/1])

If intermediate values need to be saved, writing
    Term => UnboundVariable => Next
would bind UnboundVariable to the current result value and
UnboundVariable will be accessible in the subsequent scope.

This operator could also serve as a kind of extended currying operator:
    Term => f(Var, atom, _£_, 3.14)
where the currying isn't restricted to the last of the formal parameters.

The exact syntax is of course provisional, but I hope you get the
idea. IMHO, this might be a case where the syntactic change brings
enough benefit that might outweigh the downside of having extra
syntactic constructs.

Suggestions for better syntax are most welcome and so is criticism.

best regards,
Vlad




More information about the erlang-questions mailing list