Erlang is Icky

Erlang is giving me a bad taste in my mouth for two reasons.

Before I start whining, let me make it clear that I have only just started using Erlang, and have written a total of 30 lines of Erlang code. This is just my initial impression, and I may change my mind if I ever write larger applications in the language.

The first reason I don't like Erlang is that the syntax is reminiscent of Prolog, but the semantics are lacking everything that made Prolog interesting. Fine, there's no backtracking -- I can live without that. But what I miss most is that Erlang variables are not logic variables. What I mean by this is that you can't pass around unbound variables. The only time you can use an unbound variable is on the left-hand-side of an assignment, where it will be bound immediately thereafter.

This means that Erlang functions cannot be bidirectional, the way Prolog functions can. In Prolog, you can call a function foo(A, B) in such a way that either A or B can be the input variable, and the other can be the output variable. This feature lets you do all kinds of neat things: for example, using the same function for both creating and traversing a tree structure. [Aside: In the Oz language, unbound variables are used to provide an excellent mechanism for synchronizing with worker threads.]

The second reason I don't like Erlang is its anonymous function syntax. I have yet to find a way to write them that doesn't feel exceedingly clunky. For example, here is my first cut at a function to invert an ets table:

% This function takes an ets table SrcIdx, inverts it, and stores  
% the result in the ets table DestIdx.
invert_ets(SrcIdx, DestIdx) ->
        ets:foldl(fun({Key, Vals}, _) ->
                        lists:map(fun(Val) ->
                                        [{Val, Keys}]=ets:lookup(SrcIdx, Val),
                                        ets:insert(DestIdx, {Val, [Key|Keys]})
                                  end,
                                  Vals)
                  end,
                  unused, SrcIdx),
        DestIdx.

This function is way too indented. I would have liked to move the inner fun up a level like this:

invert_ets(SrcIdx, DestIdx) ->
        InsertPair = fun(Key, Val) ->
                             [{Val, Keys}]=ets:lookup(SrcIdx, Val),
                             ets:insert(DestIdx, {Val, [Key|Keys]})
                     end,
        ets:foldl(fun({Key, Vals}, _) ->
                        lists:map(InsertPair(Key), Vals)
                  end,
                  unused, SrcIdx),
        DestIdx.

... but unfortunately this version won't work, because it requires currying, which Erlang doesn't support. The only way for "Key" to be in scope is if the InsertPair fun is nested inside the other fun.

Erlang also loses some points for having exceedingly cryptic error messages. For example, this error message is trying to tell me that the function shell_default:lookup() is not defined:

10> lookup(Q, foo).
** exited: {undef,[{shell_default,lookup,[18,foo]},
                   {shell,local_func,4},
                   {erl_eval,exprs,4},
                   {shell,eval_loop,2}]} **

To be fair though, the error messages aren't really any worse than some of the type-related error messages from hugs (a haskell implementation), and I'm sure I would become well trained at decoding them in time.

One thing I really do like about Erlang is its use of atoms pretty much everywhere. For example, in the error message above, every single word in the error message is an atom, instead of a string. I like how lots of functions return 'ok' as their result, instead of a boolean value. I'm not sure why I like atoms so much -- it's probably just a style thing.

At this point in its development, it's very obvious that Erlang grew organically, in directions that were not envisioned from the beginning. Case in point: the fact that records require header files and a preprocessor.

Posted on May 28, 2003 09:49 PM
More languages articles

Comments

The Prolog comment is a rather without direction. Of course Erlang doesn't have the semantics of Prolog. Erlang isn't a logic language.

The primary reason for the simpler semantics of Erlang is because they make it much easier to reason about real-time, real-world systems.

Posted by: James at June 4, 2003 08:42 AM

But Erlang looks like a logic language, and it was influenced by logic languages, and it was initially hosted inside of Prolog.

Perhaps I should have been clearer: precisely because of the reasons above, I was initially expecting Erlang to have some logic-language features. I was unpleasantly surprised to discover that it had none. It begs the question of why they went with Prolog-style syntax, even though they completely dropped Prolog's semantics. If you want to make a functional language, haskell has a much cleaner syntax.

Posted by: kim at June 4, 2003 09:46 AM

I too disagree with the first problem. When developing Erlang two languages were considered first, Lisp and Prolog. Both were dropped because they didn't have what was needed and so Erlang was born. The fact that the first implementation run in Prolog doesn't imply that Erlang is a logic language. It is like expecting Python to behave like C or Java to behave like C++ or clisp to behave like C, it makes no sense.
Probably the developers just knew prolog too well and decided to use it for the first prototype. Incidentally they also adopted Prolog's syntax but there's nothing inherently in the syntax that says it is a logic language and nowhere in erlang.org you are likely to find any statment saying that Erlang is a logic language.
Everywhere it says it is a functional language and I believe you can't have the features you wanted in a functional language. I think having reversible functions needs backtracking.
Furthermore, functional programming states that a function is a mapping between a set of parameters and a return value. In prolog every parameter is a possible return value. There's a contradiction.
Now, having said that, I don't like Erlang's syntaxis either and I particularly dislike the way anonymous functions are built in Erlang. It is horrible. Haskell's syntax is much, much consice and beautiful and clear.
I'd like to see Erlang's behaviour ported to Haskell, it'd be interesting.
Before you, Haskell user, jump and show me how many returs Google show with Erlang and Haskell, with papers and libraries, go and build something with those libraries to proove that they are usable.
Thakns.

Posted by: Pupeno at August 26, 2006 01:34 PM

Speaking of Haskell vs Erlang....

Posted by: Byron at August 26, 2006 02:59 PM

Sigh... This post got put on reddit. Of all the language-related posts I have, this is the one I'm least proud of. Mainly because I never followed up on the suggestions I received that I send these thoughts to an erlang mailing list. I'm tempted to just remove this post, but that seems like it would be somehow dishonest.

Anyway, hi everyone.

Posted by: Kim at August 28, 2006 09:44 PM

How about:

% This function takes an ets table SrcIdx, inverts it, and stores
% the result in the ets table DestIdx.
invert_ets(SrcIdx, DestIdx) ->
MapFun = fun(Val) ->
[{Val, Keys}]=ets:lookup(SrcIdx, Val),
ets:insert(DestIdx, {Val, [Key|Keys]})
end,
FoldFun = fun({Key, Vals}, _) ->
lists:map(MapFun, Vals)
end
ets:foldl(MapFun, unused, SrcIdx),
DestIdx.

-------------------------------------------

Records are just a 'convenience'. They are not actually part of the language specification-- they are really just tuples.

-------------------------------------------

Here's another time unbound variables can be used (pattern matching):

myfun(A, {B, {C, D, [H|T]}}) ->
do_stuff(D)...

-------------------------------------------

And isn't currying something like:

1> TimesX = fun(X) ->
fun(Y) ->
X * Y
end
end.

2> TimesThree = TimesX(3).

3> TimesThree(4).
12
4>

Posted by: daws at August 15, 2008 06:49 AM
Post a comment









Remember info?




Prove you're human. Type "human":