[erlang-questions] General advice on FP (& Erlang)

Jesper Louis Andersen <>
Sat Aug 11 21:16:29 CEST 2007


On 8/11/07, Milen Georgiev Dzhumerov <> wrote:


> And I definitely have got to ask this - how are we supposed to mutate
> state? Using processes? Function calls? I just can't wrap my head
> around how some C code is going to be translated.


If your code is a basic block, you are effectively writing in a form where
each
value you declare is 'fresh':

A1 = foo(),
A2 = bar(A1),
A3 = baz(A2).   % etc

In the above case you are lucky and able to change that into:

baz(bar(foo())).

So as long as no loops are involved, things are somewhat uncomplicated. As
soon
as you want a loop, you must think in recursion and preferably
tail-recursion at that
so you won't produce any excess stack. It will take a couple of days to wrap
your brain
around. And then you will never ever look back >:-)

Let's take a simple example. I'm writing a network app in Erlang
> which accepts "messages" on a socket, where a "message" is defined as
> length:content. If I was to do this in C, I would have a function
> which gets called every time bytes arrive on the socket and then
> depending on which state I'm in, I'm either going to append the bytes
> to a length buffer and when I reach the ":" character, I'll change
> the state and start appending to the content buffer (while
> decrementing the number of bytes still left to be read).
> Now all of this depends on having access to mutable buffers (& a
> counter) outside the scope of the functions - at this point I'm lost
> on how to do it in Erlang. How would guys start solving this problem?
> Obviously, the more experience you have, the less thinking you're
> going to put into this and the solution is going to come up
> naturally, but what about people who haven't had any real world
> experience with FP?


The above looks a lot like a bittorrent bencoding. The socket reads apart,
that
can be parsed with:

attempt_string_decode(String) ->
    {Number, Data} = lists:splitwith(charPred($:), String),
    {ParsedNumber, _} = string:to_integer(Number),
    Rest1 = tl(Data), % Skip the $:
    {StrData, Rest} = lists:split(ParsedNumber, Rest1),
    {{string, StrData}, Rest}.

Since we expect a number, we use lists:splitwith to find the first occurence
of a ':' character.
Then we are given back a Number and some Data. In the next line, we convert
the Number (which is
a string) into an integer. Then we skip the ':' character in the original
data stream. Now, the call
lists:split can break apart the Data you call 'content'. We return the
string with a tag and the part
of the original string we did not parse in this run. Subsequent calls with
different functions on this
Rest parameter can parse the following data in the String.

Note, though, that we are not taking the socket into account with the above.
If you want to do that, you must
read enough data off the socket to do the integer-part of the parse and then
read the missing amount of
bytes in order to fetch the whole message. If you have control over the
socket-format you may wish to chose a
format where data arrives as a 4-byte 32-bit big endian integer which is the
size of a message that follows. That is, the format is

<<Size:32/integer, Rest/Binary>>

where size(Rest) = Size. The reason for this is that a process controlling a
socket may then call

inet:setopts(Socket, [{active, true}, {packet, 4}]).

which will wire the socket such that Erlangs C-layer will parse the 4-byte
32-bit big endian size, fetch the message
and send it as a message to your process as {tcp, Socket, Message}. For more
information, see the kernel
documentation for inet and gen_tcp.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20070811/2d4da797/attachment.html>


More information about the erlang-questions mailing list