[erlang-questions] General advice on FP (& Erlang)
igwan
igwan@REDACTED
Sat Aug 11 22:40:45 CEST 2007
Hello Milen,
Milen Georgiev Dzhumerov a écrit :
> 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.
In FP, your state is kept in your function arguments. You call your
function with your initial state in the arguments, you do your stuff and
pass modified state (arguments) to the same function, and again ... This
is recursion and this is how you modify your state. When you reach a
certain state (certain conditions are met on your arguments), you return
the result.
>
> 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).
I'll give a quick example with comments. Let's suppose the list of
received bytes is a list. I'm not going to pass the length/content
states across a big function, but instead decompose them into two functions.
-module(simple).
-export([parse_bytes/1]).
parse_bytes(Received_bytes) ->
% We set an initial state
Length_buffer = [],
parse_length(Received_bytes, Length_buffer).
%% We handle the case where we finished parsing the "length" first
parse_length(":" ++ Rest, Length_buffer) ->
Length = list_to_integer(lists:reverse(Length_buffer)),
% we now parse the rest of our message
parse_content(Rest, Length);
%% We handle one byte of the length field
parse_length([Byte | Rest], Length_buffer) ->
% Here we accumulate our Byte in the buffer
New_length_buffer = [Byte|Length_buffer],
% And we call ourself with the modified state
parse_length(Rest, New_length_buffer).
parse_content(Bytes, Length) ->
% We need a list to accumulate the content
Content_buffer = [],
parse_content(Bytes, Length, Content_buffer).
%% We first handle the case where length is 0
%% (in the original message, or after we finished parsing the whole
Content field)
parse_content(Bytes_left, 0, Content_buffer) ->
% return Content_buffer and remaining bytes (from the next "message")
{lists:reverse(Content_buffer), Bytes_left};
parse_content([Byte | Rest], N, Content_buffer) ->
% accumulate the Byte
New_content_buffer = [Byte|Content_buffer],
% call ourself with the modified state
parse_content(Rest, N - 1, New_content_buffer).
And then call my function :
1> simple:parse_bytes("6:erlang2:is3:easy").
{"erlang","2:is3:easy"}
2> {Content, Rest} = simple:parse_bytes("6:erlang2:is3:easy").
{"erlang","2:is3:easy"}
3> Content.
"erlang"
4> Rest.
"2:is3:easy"
5> simple:parse_bytes(Rest).
{"is","3:easy"}
Then you can write a function that makes use of parse_bytes(Bytes)
calling it recursively and accumulating the result.
As a simple rule of thumb, in a functional computation, you work only
with the data you were provided in the arguments or that you obtain by
calling another function, you then do your transformations, and either
return, call yourself with a modified "state", or call another function
(as a tail call).
The control flow in Erlang is achieved with pattern-matching your
"state" with the different clauses of the function.
Hope this helps.
igwan
More information about the erlang-questions
mailing list