Ulf Wiger wrote:
> As far as possible, try to separate side-effect free code
> from that with side-effects. Typically, at least 90% of
> your code can be made side-effect free, and it's a good
> idea to really isolate the remaining 10%.
>
> This also affects your interface design. Try to make
> sure that your interfaces do not mandate side-effects.
> Compare e.g. dict.erl and ets.erl
>
> ets:insert(Tab, Obj) -> true % mandates side-effects
> dict:store(Key, Value, Dict1) -> Dict2 % doesn't
Does this mean that 90% should be implemented as a
library functions and the reminding 10% should be
isolated in a process?
> A fairly common solution to this dilemma is to use
> your own wrapper functions
>> dict_new() ->
> orddict:new().
>> etc.
>> You could also use a macro, of course:
>> -define(DICT, orddict).
>> ?DICT:store(...)
>> but I much prefer the wrapper function.
> One reason is that ?DICT can really be any
> syntactic expression, so anytime a macro is
> used, the reader has to make sure he/she knows
> what's hidden behind it.
The macro is an obvious solution I wasn't aware of. Thank you.
The problem with wrapper function without using the macro is
that change of dict type still means to change all wrapper
functions. But one interesting solution using a wrapper
module comes to my mind now:
%%% Read: dictionary interface
-module(dict_int).
%%% Common dict interface
-export([new_orddict/0, new_dict/0, store/3, fetch/2, ...]).
%% Two public "instance" creation functions.
new_orddict() -> new(orddict).
new_dict() -> new(dict).
%% Private instance creation function.
new(Impl) -> {Impl, Impl:new()}.
%% Setter function.
store(Key, Value, {Impl, Dict}) -> {Impl, Impl:store(Key, Value, Dict)}.
%% Getter functions.
fetch(Key, {Impl, Dict}) -> Impl:fetch(Key, Dict).
Using this "pattern", one could maybe come up with more
abstract interfaces, for example:
%%% Read: collection interface
-module(cltn_int)
-export([new_list/0, new_dict/0, foreach/2, ...]).
foreach({lists, Cltn}, OneArgFun) ->
lists:foreach(OneArgFun, Cltn);
foreach({Impl, Cltn}, OneArgFun) when Impl == dict or Impl == orddict ->
List = Impl:to_list(Cltn),
foreach({lists, List}, OneArgFun).
But the problem is that all this work is left to the application
programmer and so it might turn out to be not worth doing at all.
What do you think?
I will stick to macro or macro/wrapper function combination for
some time...
>> *Question:* Is there a way such polymorphism can be
>> achieved in Erlang without actually merging (see above)
>> the modules that should behave polymorphically?
>> No universally agreed upon common method, no,
> if I understood your question correctly.
>> Xmerl, for example, introduced a notion of module
> inheritance, with which one were supposed to extend
> the functionality of export callbacks. See e.g.
>http://jungerl.cvs.sourceforge.net/jungerl/jungerl/lib/xmerl/src/xmerl.erl?revision=1.2&view=markup>> mainly lines 267-313.
> (or the OTP source tree for a more current version.)
I was explicitly omiting inheritance from my set of
questions because it is not an essential OO feature
to me...
Thank you again for your anwers (you all),
Ladislav Lenart