Erlang First Post

May 27, 2008 - 5 minute read -
functional programming

Some linguists and philosophers posit the idea that you can not have a thought for which you do not have language.

"The limits of my language mean the limits of my world." -- Ludwig Wittgenstein

I've started looking at Erlang a bit. Erlang is a functional programming language that is very unlike the imperative languages which I, and many others, are most familiar. Learning a new programming language and especially a fundamentally new programming paradigm really changes the way you think about solving problems. If the linguists are to be believed, it fundamentally allows you to have no thoughts and ideas. This is what makes it so valuable for software developers to look at new languages. I thought I would share some tidbits.

Erlang Variables Aren't Variable

Erlang only allows you to set the value of a "variable" once. If you try to set it more than once the language actually throws an error.

Eshell V5.6.2  (abort with ^G)
1> X = "foo".
"foo"
2> X = "bar".
** exception error: no match of right hand side value "bar"

At first this sounds like a huge limitation to my imperative mind. In many imperative it probably would be. In Erlang it ends up not being much of an issue. In many ways it's one of the things that forces you do more idiomatic, functional Erlang.

Side Effect Free

Not being able to modify variables is one of the things that helps keep Erlang programs side effect free. Side Effects make programs harder to understand and can make them more error prone. A side effect free method is one that is "idempotent", a fancy term that means every time you call it with a given value, it will return the same result.

Thinking of side effects and how they can be reduced or removed from imperative programs can make those programs easier to understand and test.

Pattern Matching as Polymorphism

To my imperative brain that grew up mostly on Object Oriented programming languages Polymorphism and related abstraction are notions of classes and types. Erlang changes this abstraction into one of Pattern Matching. Erlang's pattern matching is almost a declarative construct like you would find in XSL. When you find call to a function that matches this pattern, use it, otherwise try the next function until you find one that matches.

To compute a Factorial, you can use 2 function signatures. The first *fac(0)* is called whenever the function is called with the integer value of zero. If the value is not zero, then that pattern is not matched and the next version is tried. In that case *fac(N)* where N is any value other than 0 is evaluated.

fac(0) ->
    1;
fac(N) ->
   N * fac(N-1).

In a slightly more complex example, you can actually pass a keyed Tuple. The key, in Erlang speak, is an atom, very similar to a Ruby symbol. Those atoms are used as part of the pattern matching to determine which function to execute.

area({square, Side}) ->
    Side * Side;
area({circle, Radius}) ->
    3.14 * Radius * Radius.

area({circle, 10}).
area({triangle, 2, 2, 2.82}).   %% error, no match

Thinking about abstractions beyond types or classes in your Object Oriented programs could open you to some interesting new solutions

Distribution Should be Explicit, Not Necessarily Hard

Distributed computing takes more work than assuming all your code will be running on a singe machine, in the same process. The Fallacies of Distributed Computing are all about the assumptions we make when we try and hide the complexity of distribution from the caller. With Java and .NET, for example, remote calls can become so hidden that they look like just another method call within the local VM. While this is convenient, it also can lead to serious problems when users don't take into account the overhead and the extra things that can go wrong with remote calls.

Erlang makes concurrent programming and spawning lots of processes to do work a very natural part of the language. Part of the language then is how to deal with the problems that arise when you run into issues talking to a remote process. The language has exception handling, it can be set up to receive a response, only wait for a timeout, etc.

The biggest thing that Erlang does is not try to hide the fact that you are communicating to a remote process (whether that process is in the same node, a different node, or a different machine). It gives you the programmer the tools to decide what conditions you need to handle based on how your program is built and deployed. All those scenarios are easy, but it's still explicit that you are talking to a different process.

ping(N, PongPid) ->
    PongPid ! {ping, self()},
    receive
        pong ->
            io:format("ping received ~B~n", [N])
    after 1000 ->
            io:format("I don't think we're gonna get a response~n")
    end,
    ping(N - 1, PongPid).
pong() ->
    receive
        finished ->
            io:format("pong finished~n", []);
        {ping, PingPid} ->
            io:format("pong received~n", []),
            PingPid ! pong,
            pong()
    end.

Think about how you can make your distribution explicit and not hidden from callers who could make bad assumptions.

I'm finding Erlang and really trying to think in a functional programming language very interesting right now, so I'll probably post some more about it. I think it will allow me to talk more intelligently to Grant at least.