Question about fun's

Ulf Wiger ulf.wiger@REDACTED
Wed Jan 27 21:23:38 CET 1999


Shawn Pearce wrote:
> 
> Ok, we're looking at implementing a system in Erlang where it would be nice to
> have true object-oriented behavior.  The way this is typically done in scheme
> is to make each object a lambda and bind the object state to the lambda when
> you construct the object.  Thus you can do "(object print)" to make it print
> out stuff.
> 
> I'm thinking that we'd do something similar in Erlang like so:
> 
> -module(myclass).
> -export([new/0]).
> 
> new() -> save_state([]).
> 
> save_state(State) ->
>         fun(Message) ->
>                 myclass:dispatch(State,Message)
>         end.
> 
> dispatch(State,{print}) -> io:format("Hi!");
> dispatch(State,{append,Item}) -> save_state([Item|State]);
> dispatch(State,{get}) -> State;
> dispatch(State,{empty}) -> save_state([]).
> 
> Then I can work with the object like this:
> 
> O=myclass:new().
> O1=O({append,TheThingy}).
> O1({print}).
> O2=O1({empty}).

Personally, I'd go with 'print' and 'empty' instead of tuples with only
one element. You don't have a type checker that's gonna complain -
Erlang really couldn't care less, but your match operation speeds up
somewhat. A matter of preference, I guess.

> 
> What are the drawbacks, ie is the fun going to cost me a large amount of RAM,
> or a large amount of processing overhead on each message?

Funs don't cost much in terms of memory. You can look at how a fun is
represented:


-module(myclass).
-export([new/0]).

new() -> 
    F = save_state([]),
    tuple_to_list(F).  %% hack to reveal the representation of a fun

save_state(State) ->
        fun(Message) ->
                myclass:dispatch(State,Message)
        end.

If you compile and run:

1> myclass:new().   
['fun',myclass,0,7662692,{[]}]

That is, a 5-tuple. The rest is compiled and represented as one
instance.

The processing overhead is roughly the same as for apply/3. A few
microseconds per call.

On the minus side:
In the current JAM implementation, error reporting for funs is lousy and
code change on-the-fly doesn't work well (the fun's internal reference
changes and the fun instance has to be recreated.)

Apart from this, I have found that it's most often counter-productive to
use an object-oriented style of programming in Erlang. Personally, I
spent my first 6 months with Erlang trying to make it object-oriented.
After that, I had convinced myself that you get further by using Erlang
as it was intended (granted, we didn't have funs in those days...)

You could do a similar thing without funs, to begin with:

-module(myclass).
-export([new/0,
         dispatch/2]).

new() -> [].

dispatch(State, print) -> io:format("~p~n", [State]);
dispatch(State, {append,Item}) -> State ++ Item;
dispatch(State, get) -> State;
dispatch(State, empty) -> [].

then:

O = myclass:new().
O1 = myclass:dispatch(O, {append,TheThingy}).

etc:

You may argue that the object-oriented aspect got lost:

-module(myclass).
-export([new/0,
         dispatch/2]).


new() ->
   {?MODULE, []}.

dispatch(State, print) -> io:format("~p~n", [State]);
dispatch(State, {append,Item}) -> State ++ Item;
dispatch(State, get) -> State;
dispatch(State, empty) -> [].

-module(oo).
-export([msg/2]).

msg({Class, State}, Msg) ->
   Class:dispatch(State, Msg).  %% this uses apply/3 instead of funs

then:

O = myclass:new().
O1 = oo:msg(O, {append,TheThingy}).
O2 = oo:msg(O1, print).
...


Useless exercise?

Perhaps. What you gain is that you avoid the negative aspects (bugs?) of
funs, at little syntactical expense. I have written code like that.
Sometimes, I find it justified (when the OO paradigm really adds
advantages), but most of the time, it just makes the code harder to
read, harder to write, and harder to maintain.

So what do I propose?

Look at modules like:

lists (lists:append(L1, L2), lists:reverse(L), ...)
queue (queue:new(), queue:in(Q, Item), queue:out(Q), ...)
application (application:start(Name), application:stop(Name), ...)

and so on. The big advantage of this style of programming is that
programs become much easier to read, since the semantics are obvious.
You still get a great deal of reuse, and your productivity will be very
high.

I admit I haven't the faintest idea of what kind of program you're
trying to write. Perhaps your problem domain really calls for
object-oriented design? 

If so, forgive me, but it's very common for programmers new to Erlang to
go overboard trying to make everything object-oriented. 

/Uffe
--
Ulf Wiger, Chief Designer AXD 301     <ulf.wiger@REDACTED>
Ericsson Telecom AB                          tfn: +46  8 719 81 95
Varuvägen 9, Älvsjö                          mob: +46 70 519 81 95
S-126 25 Stockholm, Sweden                   fax: +46  8 719 43 44



More information about the erlang-questions mailing list