Erlang Examples: GUIs and Error Handling

This is part of a series on the Erlang Exercises which is a great set of programming problems that challenge you to implement solutions to some common Erlang problems. I’m going to share some of my solutions to these problems.

Robustness in Erlang, and use of a graphics package

Excercise: Create a window containing three buttons: Quit , Spawn , Error.
The Spawn button shall create a child process which displays an identical window.
The Quit button should kill the window and its child windows.
The Error button should cause a runtime error that kills the window (and its children), this window shall then be restarted by its parent.

-module(gui).
-export([init/0]).
 
init() ->
    S = gs:start(),
    Win = gs:create(window, S, [{width, 350}, {height, 100}]),
    gs:create(button, quit, Win, [{label, {text, "Quit"}}, {x, 0}]),
    gs:create(button, spawn, Win, [{label, {text, "Spawn"}}, {x, 100}]),
    gs:create(button, error, Win, [{label, {text, "Error"}}, {x, 200}]),
    gs:config(Win, {map, true}),
    loop().
 
loop() ->
    receive
        {gs, spawn, click, _, _} ->
            Pid = spawn(?MODULE, init, []),
            handle_error(self(), Pid),
            io:format("got here~n",[]),
            loop();
        {gs, quit, click, _, _} ->
            io:format("quitting~n",[]),
            gs:stop();
        {gs, error, click, _, _} ->
            erlang:error(errorclick);
        exit ->
            bye
    end.
 
handle_error(MasterPid, Pid) ->
    spawn(fun() ->
                  process_flag(trap_exit, true),
                  link(Pid),
                  receive 
                      {'EXIT', Pid, {errorclick, _}} ->
                          io:format(" ~p died :~n",[Pid]),
                          MasterPid ! {gs, spawn, click, a, b};
                      _ ->
                          io:format(" really quitting :~n", []),
                          MasterPid ! exit
                  end
          end).

gs

The gs code is all GUI code. It just goes to show that you can use FP to create GUIs even if it’s not quite as intuitive as it might be in an OO language. It creates a windows with a series of buttons on it.

handle_error

This handle_error code shows linking a Process to another error handler process. That spawns a process whose sole purpose is to listen for errors from the main process. If an {‘EXIT’, Pid, {errorclick, _}} error is received the error handler sends a message back asking the MasterPid to spawn a new window.

Erlang Examples: Talk with Erlang

This is part of a series on the Erlang Exercises which is a great set of programming problems that challenge you to implement solutions to some common Erlang problems. I’m going to share some of my solutions to these problems.

Implementing Talk with Distributed Erlang

Make a simple Talk program that makes it possible to chat with friends at other nodes/hosts.
Hints: Your program should consist of two registered processes one for reading from the terminal and the other one for recieving messages from the other node then writing them to the terminal.

-module(talk2).
-compile(export_all).
start() ->
    OtherNode = clean(io:get_line('What node? ')),
    FullNode = string:concat(OtherNode, "@localhost"),
    io:format("talking to: ~s~n", [FullNode]),
    register(receiver, spawn(?MODULE, receiver, [])),
    register(sender, spawn(?MODULE, sender, [list_to_atom(FullNode)])),
    get_input().
get_input() ->
    Message = clean(io:get_line('Talk: ')),
    case string:equal("exit!", Message) of
        true ->
            receiver ! done,
            sender ! done;
        false ->
            talk(Message),
            get_input()
    end.
talk(Message) ->
    sender ! {send, Message}.
sender(OtherNode) ->
    receive
        {send, Message} ->
            rpc:call(OtherNode, talk2, send_message, [Message]),
            sender(OtherNode);
        done ->
            void
    end.
send_message(Message) ->
    receiver ! {message, Message}.
receiver() ->
    receive
        {message, Message} ->
            io:format("~s~n", [Message]),
            receiver();
        done ->
            void
    end.
clean(Data) ->
    string:strip(Data, both, $\n).

Explanation

This is functionally similar to Erlang Talk with Sockets. The difference is that it is doing Erlang-to-Erlang only communication taking advantage of Erlang’s distributed functionality. The Socket example could have been made to interact with any programming language that can do socket programming.

start() ->
This starts the application by asking the user for the name of the other Erlang node to talk to. Erlang nodes are started by running them with a node name erl -name foo. That along with host information are used to talk to a remote node.

So, first using io:get_line(‘What node? ‘) we ask the user then name of the other node. Then we spawn 2 processes. One to receive messages and one to send them. The interesting part here is the use of the register function. It takes an atom and a process id. The register function basically binds a pid to an atom and creates a globally accessible pid. It just simplifies the code a bit so you don’t have to pass those pids to all of the other functions.

get_input() ->
This is the main user input loop. It asks the user for a message and then sends that message to the sender process. io:get_line(‘Talk: ‘) waits for user input before it returns, so only after a user types something and hits return will this function continue. If the case string:equal(“exit!”, Message) do evaluates to true, then the sender and receiver processes are told to exit and the program ends naturally. Otherwise the message is sent.

talk(Message) ->
This function sends the message to the sender process.

sender(OtherNode) ->
This is the function that is running as the sender process. It is initialized with the remote OtherNode information. It receives messages and forwards them to the OtherNode. The rpc:call(OtherNode, talk2, send_message, [Message]), call invokes the send_message function running on the remote node.

send_message(Message) ->
This is really an RPC helper method that passes messages it get to the process running the receive function.

receiver() ->
The receiver is the function that is being run as the receiver process. It listens for messages and when it gets one it prints it out to the user.

Thoughts

The other interesting way to solve this would be to actually spawn processes remotely on the other node. The code can then have a reference to a pid running on the remote node and can pass messages to it directly without using the RPC approach. This would allow one side of the conversation to initiate it without the other knowing about it.


register(remote_receiver, spawn(OtherNode, talk2, receiver, [])),
remote_receiver ! "Hello".

An exercise I’ll leave up to you!

Erlang Examples: Talk with Sockets

This is part of a series on the Erlang Exercises which is a great set of programming problems that challenge you to implement solutions to some common Erlang problems. I’m going to share some of my solutions to these problems.

Erlang using UNIX sockets

Do you want to talk with a friend on another machine? Shouldn’t it be nice to have a shell connected to your friend and transfer messages in between?
This can be implemented using the client/server concept with a process on each side listening to a socket for messages.

-module(talk).
-compile(export_all).
start(LocalPort, RemotePort) ->
    ServerPid = spawn(?MODULE, start_server, [LocalPort]),
    cli(RemotePort, ServerPid).
cli(RemotePort, ServerPid) ->
    Data = clean(io:get_line('Message: ')),
    case should_stop(Data) of
        true ->
            ServerPid ! done;
        false ->
            spawn(?MODULE, send, [RemotePort, Data]),
            cli(RemotePort, ServerPid)
    end.
clean(Data) ->
    string:strip(Data, both, $\n).
should_stop(Str) ->
    0 =:= string:len(Str).
start_server(Port) ->
    case gen_tcp:listen(Port, [binary, {packet, 4},
                               {reuseaddr, true},
                               {active, true}]) of
        {ok, Listen} ->
            server(Listen);
        {error, Reason} ->
            {error, Reason}
    end.
server(Listen) ->
    case gen_tcp:accept(Listen) of
        {ok, Socket} ->
            server_loop(Socket),
            server(Listen);
        _ ->
            ok
    end.
server_loop(Socket) ->
    receive
        done ->
            gen_tcp:close(Socket),
            io:format("Server socket closed~n");
        {tcp, Socket, Bin} ->
            Str = clean(binary_to_term(Bin)),
            io:format("~p~n", [Str]),
            case should_stop(Str) of
                true ->
                    void;
                false ->
                    server_loop(Socket)
            end;
        {tcp_closed, _Socket} ->
            ok
    end.
send(Port, Data) ->
    case gen_tcp:connect("localhost", Port, [binary, {packet, 4}]) of
        {ok, Socket} ->
            gen_tcp:send(Socket, term_to_binary(Data)),
            gen_tcp:close(Socket);
        {error, Reason} ->
            {error, Reason}
    end.

Explanation

start(LocalPort, RemotePort) ->
The entry point to the talk program. This spawns a new start_server process that will handle opening a socket and listening for messages from a remote client. It then runs the command line interface and waits for data from the user to send to the remote port.

cli(RemotePort, ServerPid) ->
This is the loop that will get the data from the user and send it to the remote port.

clean(Data)
This is a simple function that removes the newlines from the end of the data sent to the client.

should_stop(Str)
This is a simple function that determines whether the client and server should shut down. The rule is that if it’s an empty string then the processes should stop.

start_server(Port) ->
This function gets run in a process and opens a Listening socket. It then hands off to server(Listen) -> to handle dealing with new incoming socket connections.

server(Listen) ->
The server gets a new Socket when an incoming connection connects to the Listening port. It then goes into a receive state and waits for an incoming connection to send it data. The receive state is handled in the server_loop.

send(Port, Data) ->
This is the client side of the application. It connects to a remote port and sends the data to that port.

case … of
In a number of these functions you see a common idiom:

case fun() of
    SomeMatch ->
        Do something;
    AnotherMatch ->
        Do something else
end

With socket connections and many others it is common to return a tuple like {ok, Val} when the function call is successful and will return {error, Reason} or a similar tuple when there is an error in the function. The case statement is just an easy way to handle these differences.

Erlang Example: Star Messages

This is part of a series on the Erlang Exercises which is a great set of programming problems that challenge you to implement solutions to some common Erlang problems. I’m going to share some of my solutions to these problems.

Interaction between processes, Concurrency

3) Write a function which starts N processes in a star, and sends a message to each of them M times. After the messages have been sent the processes should terminate gracefully.

-module(star).
-export([start/2, loop/0, sendMessage/2]).
start(0, _) ->
    io:format("done~n", []);
start(N, M) ->
    timer:sleep(random:uniform(10) * 100),
    LoopPid = spawn(?MODULE, loop, []),
    spawn(?MODULE, sendMessage, [M, LoopPid]),
    start(N - 1, M).
sendMessage(0, Pid) ->
    Pid ! done;
sendMessage(M, Pid) ->
    timer:sleep(random:uniform(5) * 100),
    Pid ! M,
    sendMessage(M - 1, Pid).
loop() ->
    receive
        done ->
            io:format("* Process: ~10w, Message#: terminate!~n", [self()]);
        R ->
            io:format(": Process: ~10w, Message#: ~w ..~n", [self(), R]),
            loop()
    end.

Explanation

start(N, M)
The entry point to the code defines the Number of processes to start and the number of Messages to pass. timer:sleep(random:uniform(10) * 100), is just to demonstrate that things are actually happening concurrently. Without this the program usually runs to fast and everything is sequential. LoopPid = spawn(?MODULE, loop, []), creates a new process running the loop function. That process is set up to receive messages and print some information out. spawn(?MODULE, sendMessage, [M, LoopPid]), is then called to create a new process whose job it is to send each LoopPid (really the nodes in the star) M number of messages. Finally start(N – 1, M). is called tail recursively with one less Node count.

start(0, _)
This function is called or matched when the tail recursion from start(N, M) finally gets down to the value where N is zero. In that case we no longer want to spawn processes so we just write “done” and exit the function.

sendMessage(M, Pid)
Following a similar pattern to the start function this counts down M to zero and sends M as a message to the given Pid. The tail recursion handles decrementing to the count of M until the pattern (0, Pid) is matched at which point sendMessage(0, Pid) is matched and the Pid is sent the ‘done’ message.

loop()
This is the function that is run as the individual processes or the “points of the star”. Its job is to receive messages passed to it. When done -> is matched by a message, the process will print out some information and then end gracefully. R -> represents a message being passed with a single value that is anything other than the atom ‘done’. When it is matched the value is printed and the process waits for another message by calling back to itself with loop()

Thoughts

One of the interesting things to me is the tail recursion of Erlang and other Functional Programming Languages. This accomplished the same thing as a looping construct without using for or while or any of those usual imperative methods.

Erlang Example: Min and Max Element of a List

This is part of a series on the Erlang Exercises which is a great set of programming problems that challenge you to implement solutions to some common Erlang problems. I’m going to share some of my solutions to these problems.

Simple recursive programs

1. Write a function lists1:min(L) which returns the mini- mum element of the list L.
2. Write a function lists1:max(L) which returns the maximum element of the list L.

(I’m only showing the max version since the min is basically just the change of the guard clause.)

-module(mylists).
-export([max/1]).
max([H|T]) ->
    max(H, T).
max(M, []) ->
    M;
max(M, [H|L]) when M > H ->
    max(M, L);
max(_M, [H|L]) ->
    max(H,L).

Explanation

max([H|T])
This code is composed of a public, exported function and a private function. max([H|T]) defines a function that takes a list. The slightly funny notation [H|T] is an operation that removes the head value (the zeroth element] from a list and assigns the head value to H and the remainder of the list to T. Think of the list as a stack, and you’ve just popped the stack. This method then delegates the remainder of the work to the internal, 2 value max function.

max(M, [H|L]) when M > H ->
This method is the main part of the internal, 2 value max function. The interesting piece here is when M > Hmax(_M, [H|L]) -> which acts as a fall-through because the ones that don’t match the first will call this. You can see those two functions take either M or H and pass that as the current Max value.

max(M, []) ->
The final piece of the internal max function is max(M, []) ->. This is the end state of the function. The [] clause in the function arguments pattern matches an empty list. Based on those previous 2 parts of the function definition this is fulfilling the final case where the current max value has been compared to the last element in the list.

Example

List = [1,2,4,6,5].
mylists:max(List).

So what happens is:

  1. max([1 | [2,4,6,5]])
  2. max(1, [2 | [4,6,5]])
  3. max(2, [4 | [6,5]])
  4. max(4, [6 | [5]])
  5. max(6, [5 | []]) when M > H
  6. max(6, [])

Erlang Exercises: Ring Messages

Erlang Exercises has a great little series of programming problems that challenge you to implement solutions to some common Erlang problems. It’s a great way to learn the language. I’m going to share some of my solutions to these problems. Maybe for some discussion, maybe for some feedback from some people more experienced with Erlang, maybe just to give some new people a flavor of the language.

Interaction between processes, Concurrency

2) Write a function which starts N processes in a ring, and sends a message M times around all the processes in the ring. After the messages have been sent the processes should terminate gracefully.

-module(ring).
-export([start/2, loop/2]).
start(N, M) ->
    Seq = lists:seq(1, N),
    Messages = lists:reverse(lists:seq(0, M-1)),
    LastP = lists:foldl(fun(S, Pid) -> 
                                             spawn(?MODULE, loop, [N-S, Pid]) 
                             end, self(), Seq),
    spawn(fun() -> [ LastP ! R || R <- Messages ] end).
loop(N, NextPid) ->
    receive
        R when R > 0 -> 
            NextPid ! R,
            io:format(": Process: ~8w, Sequence#: ~w, Message#: ~w ..~n", 
                          [self(), N, R]),
            loop(N, NextPid);
        R when R =:= 0 ->
            NextPid ! R,
            io:format("* Process: ~8w, Sequence#: ~w, Message#: terminate!~n", 
                            [self(), N])
    end.

Explanation

Seq = lists:seq(1, N) and Messages = lists:reverse(lists:seq(0, M-1)),
These create lists of integers from 1 – N and then from M-1 – 0 (because of the reverse). These lists are used for creating processes and the messages that will be passed to those processes respectively.

LastP = lists:foldl(…
This is an accumulator function. The self() value passed to the function is passed in as Pid on the first iteration, but subsequent iterations get the value computed in the method with the spawn function. The final spawn Pid is returned from the accumulator and is stored in LastP. The spawn function is setting up new processes which are running the loop function with the given values.

spawn(fun() -> [ LastP ! R || R <- Messages ] end).
This is a list comprehension in Erlang. It basically takes each value from the Messages list and sends that as a message to the LastP pid which is the beginning of the Ring.

loop(N, NextPid) ->
This is the function that is being run as the processes of each of the elements of the ring.

R when R > 0 ->
When this process receives a message where the message is an integer greater than zero then write some info to the console and forward the message to the NextPid and use tail recursion to start waiting for another message.

R when R =:= 0 ->
When this process receives a message where the message is an integer that equals zero then write some info to the console and forward the message to the NextPid, but allow this process to complete naturally.