Erlang polymorphism: multiple implementations of the same contract? - erlang

What's correct Erlang way to have separated implementations from the contract and how to switch between them?

While others mention the behaviour feature, it is merely a small helper to make sure you implement all functions in a module for a callback structure. If you have two implementations, a and b, and both implements the same functions, you can just statically substitute a for b in a calling module. For static configuration where you have a better implementation, this is preferable.
If the question is of a more dynamic nature, you can just do
Mod = a,
Mod:f(Args).
And then in code set Mod appropriately. This lets you dynamically control what module to call while the program is running. It is not entirely clear which of the two you want.

Nice example of polymorphism is qlc module and structure table. See various M:table/1,2 implementations in ets, dets, mnesia and so. Try ets:table(ets:new(foo, [set])). in shell for example and look into qlc documentation and examples.

Since Erlang is dynamically typed, function guards (the when … -> bits) are the way to express polymorphism.
E.g:
len (T) when is_tuple(T) -> size(T);
len (L) when is_list(L) -> length(L).

Maybe have a look on behaviours concept. At least for me there is small similarity to OOP in terms of having interface definition and multiple implementation modules.

If I realized your question, here is example for approach, that pretty much works for me. This approach helps to separate interface and implementation.
"Interface" module.
-module(contract).
-export([
new/2,
do_something/2
]).
%% Behavioural callbacks definition. Each of "derived" modules should implement it.
-callback new(Arg :: any()) -> {ok, ImplState :: any()} | {error, Reason :: atom()}.
-callback do_something( Arg :: any(), ImplState :: any() ) -> {ok, ReturnVal :: any(), NewImplState :: any()} | {error, Reason :: atom()}.
%% Opaque state to hold implementation details
-record(
contract_impl, {
impl_module :: module(),
impl_state :: any()
}
).
%% Interface for creation "polymorphic" instance, like base-class constructor.
new(ImplModule, ImplInitArgs) ->
case ImplModule:new(ImplInitArgs) of
{ok, State} ->
{ok,
#contract_impl {
impl_module = ImplModule,
impl_state = State
}
};
{error, Reason} ->
{error, Reason}
end.
%% Interface function, like an abstract method in OOP.
do_something(
Arg,
#contract_impl {
impl_module = ImplModule,
impl_state = ImplState
} = ContractImpl
) ->
case ImplModule:do_something(Arg, ImplState) of
{ok, ReturnVal, NewState} ->
{ok, ReturnVal, ContractImpl#contract_impl{ impl_state = NewState }};
{error, Reason} -> {error, Reason}
end.
Some implementation example (like derived class).
-module(foo).
-behaviour(contract).
-export([
new/1,
do_something/2
]).
-record(
foo_state, {
repeat_count
}
).
new(Options) ->
{ok,
#foo_state{
repeat_count = proplists:get_value(repeat_count, Options)
}
}.
do_something(Arg, #foo_state{ repeat_count = RepeatCount } = State) ->
Result = [ io_lib:format("Foo ~p", [Arg]) || _Index <- lists:seq(1, RepeatCount) ],
{ok, Result, State}.
Now you can do the following:
usage_example() ->
{ok, State} = contract:new(foo, [{repeat_count, 15}]),
{ok, Result, NewState} = contract:do_something("bar", State),
ok.
I hope this helps.

Related

Erlang BIFs inside lists module

Inspired from this question, I was curious to see how lists:reverse/2 is implemented in the source code inside lists.erl module.
I found out that there is no implementation for lists:reverse/2 inside lists.erl, but there is an implementation for lists:reverse/1 that uses lists:reverse/2:
reverse([] = L) ->
L;
reverse([_] = L) ->
L;
reverse([A, B]) ->
[B, A];
reverse([A, B | L]) ->
lists:reverse(L, [B, A]).
At the top of the file there are some lines that tells that lists:reverse/2 (and some other functions) are BIFs:
%%% BIFs
-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]).
...
%% Shadowed by erl_bif_types: lists:reverse/2
-spec reverse(List1, Tail) -> List2 when
List1 :: [T],
Tail :: term(),
List2 :: [T],
T :: term().
reverse(_, _) ->
erlang:nif_error(undef).
Question: First, I couldn't find the actual implementations of those BIFs. Where can I find them? Second, If someone also knows to explain why is it organized that way?
The lists BIFs are implemented in erts/emulator/beam/erl_bif_lists.c. Parts of heavily-used standard modules such as lists are implemented as BIFs for efficiency and performance.

How to document a type as just a name?

Looking at Erlang docs, sometimes I come across type specs, that document a function type without really specifying the type...
Hard to explain, so let me give you an example. In the gen_server the handle_call function shows up as the following in the docs:
Request = term()
From = {pid(),Tag}
State = term()
Result = {reply,Reply,NewState} | {reply,Reply,NewState,Timeout}
| {reply,Reply,NewState,hibernate}
| {noreply,NewState} | {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {stop,Reason,Reply,NewState} | {stop,Reason,NewState}
Reply = term()
NewState = term()
Timeout = int()>=0 | infinity
Reason = term()
Here the type Tag is never shown (i.e. it's just a variable name).
Is the same possible to do with edoc? The closest I found was opaque type spec, but that documents it as abstract - is that the same?
It is possible as long as the argument variable name in the function (in all clauses) and that name used in the #spec are same. For example.
%% #spec start_link(Args) -> {ok, pid()}
start_link(Args) ->
gen_server:start_link(?MODULE, Args, []).
will generate the following doc
start_link(Args) -> {ok, pid()}
If you have a different name for example
%% #spec start_link(Args) -> {ok, pid()}
start_link(Num) when is_integer(Num) ->
gen_server:start_link(?MODULE, [], []);
start_link(Args) ->
gen_server:start_link(?MODULE, Args, []).
It would generate as
start_link(Num::Args) -> {ok, pid()}
assuming Args as a type.
I would personally not recommend to use edoc #spec and instead use -spec as they are used by dialyzer and hence can be validated.
edoc can generate the doc from -spec too. Given both -spec and #spec, edoc overrides to #spec directive.
%% #spec start_link(Args) -> {ok, Pid}
-spec start_link(term()) -> {ok, pid()}.
start_link(Args) ->
gen_server:start_link(?MODULE, Args, []).
Above will result in
start_link(Args) -> {ok, Pid}
And removing #spec from above will result in
start_link(Args::term()) -> {ok, pid()}
As far as I can see, this is not possible with Edoc.
I tried using a spec like this:
-spec foo(Something) -> term().
foo(X) ->
X.
And while Edoc is actually happy about it, and gives the type spec as foo(Something) in the docs without specifying what Something is, the compiler complains about it, and says:
type variable 'Something' is only used once (is unbound)
So you'd have to write:
-spec foo(Something::_) -> term().
foo(X) ->
X.
which shows up as foo(Something::term()), thereby giving a type specification for Something.
Using an Edoc-only type specification:
%% #spec foo(Something) -> term()
foo(X) ->
X.
Edoc treats Something as a type name that applies to the variable X and puts foo(X::Something) in the documentation.
So neither way would do exactly what you want to achieve.

Pattern not matching in Erlang

I'm making this call:
add(Login, Pass, Role) ->
gen_server:call(?SERVER, {add, Login, Pass, Role}).
and I expect it to match with:
handle_call(State, {add, Login, Pass, Role}) ->
io:format("add ~n"),
Db = State#state.db,
case lists:keyfind(Login, 1, Db) of
false->
io:format("add - reg new ~n"),
{reply, registered, State#state{db=[{Login, erlang:md5(Pass), Role, ""}|Db]}};
{Key, Result}->
{reply, invalid_params, Db}
end.
but it always goes to:
handle_call(_Request, _From, State) ->
io:format("undef ~n"),
Reply = ok,
{reply, Reply, State}.
What's wrong?
The behaviour seems valid,
handle_call has such spec:
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
State :: #state{}) ->
{reply, Reply :: term(), NewState :: #state{}} |
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
{noreply, NewState :: #state{}} |
{noreply, NewState :: #state{}, timeout() | hibernate} |
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
{stop, Reason :: term(), NewState :: #state{}}).
If you can take a look here
http://erlang.org/doc/man/gen_server.html#Module:handle_call-3
Also, for otp default behaviours, it would be the best, as a start, to use templates. For gen_server eg https://gist.github.com/kevsmith/1211350
Cheers!
In a module using the gen_server behaviour, the handle_call callback function should take three arguments. However, you have defined two different functions, handle_call/2 and handle_call/3. (In Erlang, functions that have the same name but take different numbers of arguments are considered different functions.)
Since the gen_server module only looks for handle_call/3 and ignores handle_call/2, your "undef" function is always called.
To fix this, change the function to take an (ignored) second argument, and put the request first and the state last:
handle_call({add, Login, Pass, Role}, _From, State) ->
and change the end. to end; — . separates different functions, while ; separates different clauses of the same function.

Erlang clustering and gproc

I'm a little confused about clustering with gproc as a pubsub.
I would like to hold client sessions with gproc... It works great with one node.
However, I need to cluster the whole system.
It seems (as far as I undertand) that gproc has 2 way working with clusters, setting it as global, or using gproc_dist which seems to be a gen_leader behaviour.
Am I right so far ?
What would be the downside of each method ? (Still assuming I undertood correctly)
I think the global is implemented by gen_leader. so they are the same method.
the gproc_dist_tests.erl already give the sample code for explanation.
dist_test_() ->
{timeout, 120,
[{setup,
fun() ->
Ns = start_slaves([dist_test_n1, dist_test_n2]),
?assertMatch({[ok,ok],[]},
rpc:multicall(Ns, application, set_env,
[gproc, gproc_dist, Ns])),
?assertMatch({[ok,ok],[]},
rpc:multicall(Ns, application, start, [gproc])),
Ns
end,
fun(Ns) ->
[rpc:call(N, init, stop, []) || N <- Ns]
end,
fun(Ns) ->
{inorder,
[
{inparallel, [
fun() ->
?debugVal(t_simple_reg(Ns))
end,
fun() ->
?debugVal(t_simple_counter(Ns))
end,
fun() ->
?debugVal(t_aggr_counter(Ns))
end,
fun() ->
?debugVal(t_update_counters(Ns))
end,
fun() ->
?debugVal(t_shared_counter(Ns))
end,
fun() ->
?debugVal(t_mreg(Ns))
end,
fun() ->
?debugVal(t_await_reg(Ns))
end,
fun() ->
?debugVal(t_await_self(Ns))
end,
fun() ->
?debugVal(t_await_reg_exists(Ns))
end,
fun() ->
?debugVal(t_give_away(Ns))
end,
fun() ->
?debugVal(t_sync(Ns))
end,
fun() ->
?debugVal(t_monitor(Ns))
end,
fun() ->
?debugVal(t_subscribe(Ns))
end
]
},
fun() ->
?debugVal(t_sync_cand_dies(Ns))
end,
{timeout, 90, [fun() ->
?debugVal(t_fail_node(Ns))
end]}
]}
end
}]}.
I think gproc is created for solving the erlang's process register restriction. That's "only atom not tuple" can be used as registered key and one and only one process can be registered for one registered key.
The erlang has already provided mnesia db etc for solving client session data.
If the client session data throughout is high, I think it is not good idea to use gproc to handle it, for it will delay the process registration.

Erlang : Which is more efficient in this scenario - using try catch or case statements?

Say I have some function fn1() in Erlang which returns {ok, Result} if the function was executed successfully and {error, "ErrorReason"} if there was an error.
Now in another function fn2() I call fn1() and I need to check the result of fn1 and proceed only if it is {ok, Result}.
I figured, I can do this using either case or try catch. But Efficiency is my main concern and I'd like to know which of the two methods below is more efficient:
try-catch Method
fn2() ->
try
{ok, Result} = fn1(),
%Do something with Result
ok
catch
throw:Term -> Term;
exit:Reason -> {exit, Reason};
error:Reason -> {error,{Reason,erlang:get_stacktrace()}}
end.
case Method
fn2() ->
Res = fn1(),
case Res of
{ok, Result} ->
%Do something with Result
ok;
{error, Reason} ->
Reason
end.
You really want to try and avoid try/catch like the plague. It is a very uncommon idiom in Erlang - really only used in a couple of special cases:
where you are checking user-supplied
input and you have no guarantees that
it will be 'correct'
where you have something which is
deeply nested and the cost of
unrolling it on an error condition
is too expensive
like mensia transactions
or in parser/lexer's
Try/catch is essential in languages like C++ where the application is unstable in the presence or errors, but Erlang is stable in those circumstances - the process crashes but doens't bring the system down.
You should programme the happy path, match return values and if the application deviates from what you expect then let it crash. The crash tells you you have a problem and tells you to fix it.
The problem with try/catch is that it can simply mask the problem, or even worse, move the eventual crash away from where it should happen (inside the expression you have wrapped) and make it appear elsewhere - where your programming logic expects it to have suceeded = which makes debugging much harder.
Programming the happy path and letting it crash is very disconcerting the first time you do it, it feels like going out with no clothes on, but actually you get used to it real quick :)
The case method will be more efficient, as it simply pattern matches, and does not involve building a call stack and stuff.
In both examples you are about to handle the "error" locally, so there is no point in the try catch.What you might see sometimes is something like:
fn2() ->
{ok, Result} = fn1(),
%Do stuff with Result
ok.
Here the intention is that you make fn2() throw a badmatch, if fn1() did not return ok. You let someone else "above" handle the problem. E.g. this might kill your process, and make your supervisor create a new one.
You should always measure to find things like this out.
Your code also does not do what you think it does.
-module(glurk).
-compile(export_all).
fn2() ->
try
{ok, Result} = fn1(),
%Do something with Result
ok
catch
throw:Term -> Term;
exit:Reason -> {exit, Reason};
error:Reason -> {error,{Reason,erlang:get_stacktrace()}}
end.
fn1() ->
{error, a}.
Try this out:
c(glurk).
./glurk.erl:6: Warning: variable 'Result' is unused
{ok,glurk}
16> glurk:fn2().
{error,{{badmatch,{error,a}},
[{glurk,fn2,0},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
This is because fn1 did not raise an exception
it gebnerated a normal retyurn value {error, a} which
does not pattern match against {ok, Result}
The first version of your code works with a function that either returns a normal value
or raises an exception - you have to write it like this:
fn1(....) ->
...
%% success case
Val;
%% failure case
throw(...) | exit(...) | error(...)
You can't just pump the same function into fn1 and fn2.
If you had the case where the called function had to escape from a deep recursion
then the first method would be more efficient than the second - since you could
immediately exit from a deep recursion by saying throw(...).
So the answer depends upon the nature of the function that you are calling.
Code should always be optimised for beauty and not efficiency - since you have
to maintain the stuff - then it should only be optimised in the rare cases
where it is not fast enough. What needs to be optimised should be identified
by measuring the program (you will always be surprised here :-)
Me, I'd write
{ok,Result} = ...
Actually your first code has a more subtle error
fn2() ->
try
{ok, Result} = fn1(),
%Do something with Result
ok
catch
throw:Term -> Term;
exit:Reason -> {exit, Reason};
error:Reason -> {error,{Reason,erlang:get_stacktrace()}}
end.
Think about this. The caught error cases do not themselves handle the error
they just return tuples like {exit, Reason} or {error, Reason} this means that the
next layer up (ie the caller of fn2) will also have to mess around checking
error returns - if this is repeated at all levels the code will be a mess.
The "erlang" way is to have one try-catch at the top of the program and just terminate
abruptly with exit(Why) if an error occurs.
In fact often you should not even do this - you should link your process to another process
then the offending process will die and "the other processes will fix the error".
The exception propagates up the call stack and flies over to the linked processes
for treatment. So we have two types of processes - ones that have no inbuilt error handling
and processes that only do error handling.
In this case, irrespective of what is more efficient, you should definitely use the case alternative as it more succinctly describes what is going on. Your fn1() here return a value indicating if there is a successful value or an error. In the case version you directly match against this, while in the try version you match against the success value which will generate an error if an error was returned. It is unnecessarily convoluted and hides what is going on so it is bad programming style and should be avoided.
And as Gordon has already pointed out having a try there will catch more errors than you probably intend and may so mask other real errors which you should see.
The case will also be faster here, but the difference is probably small. The clarity, succinctness and good programming style is much more important!
The case will always be more efficient, but the difference is small and it's more important what makes more sense in your particular case. Especially, which approach produces more understandable code. See this benchmark:
%% Results:
%% 7> errors:run_normal(100).
%% {9,ok}
%% 8> errors:run_normal(1000).
%% {107,ok}
%% 9> errors:run_normal(10000).
%% {856,ok}
%% 10> errors:run_normal(1000, 10).
%% {263,ok}
%% 11> errors:run_wcatch(10000).
%% {2379,ok}
%% 12> errors:run_wcatch(1000, 10).
%% {401,ok}
%% 18> errors:run_normal_cplx(10000, 50).
%% {7910,ok}
%% 19> errors:run_wcatch_cplx(10000, 50).
%% {10222,ok}
-module(errors).
-compile(export_all).
run_normal(Iterations) ->
get_result(Iterations, fun() -> normal() end).
run_normal(Iterations, Level) ->
get_result(Iterations, fun() -> deepnormal(Level) end).
run_wcatch(Iterations) ->
get_result(Iterations, fun() -> wcatch() end).
run_wcatch(Iterations, Level) ->
get_result(Iterations, fun() -> deepwcatch(Level) end).
run_normal_cplx(Iterations) ->
get_result(Iterations, fun() -> normal_complex() end).
run_normal_cplx(Iterations, Level) ->
get_result(Iterations, fun() -> deepnormal_complex(Level) end).
run_wcatch_cplx(Iterations) ->
get_result(Iterations, fun() -> wcatch_complex() end).
run_wcatch_cplx(Iterations, Level) ->
get_result(Iterations, fun() -> deepwcatch_complex(Level) end).
%%------------------------------------------------------------------------------
get_result(Iterations, Fun) ->
timer:tc(fun() -> run(Iterations, Fun) end).
run(0, _Fun) ->
ok;
run(Iterations, Fun) ->
Fun(),
run(Iterations - 1, Fun).
%%------------------------------------------------------------------------------
normal() ->
case foo(atom) of
{ok, atom} -> ok;
{error, atom} -> ok
end.
normal_complex() ->
case foo_cplx() of
{ok, Res} -> Res;
{error, Res} -> Res
end.
deepnormal(Level) ->
case deepfoo(atom, Level) of
{ok, atom} -> ok;
{error, atom} -> ok
end.
deepnormal_complex(Level) ->
case deepfoo_cplx(Level) of
{ok, Res} -> Res;
{error, Res} -> Res
end.
wcatch() ->
try
{ok, atom} = foothrow(atom)
catch
throw:{error, atom} -> ok
end.
wcatch_complex() ->
try
{ok, _Res} = foothrow_cplx()
catch
throw:{error, Res} -> Res
end.
deepwcatch(Level) ->
try
{ok, atom} = deepfoothrow(atom, Level)
catch
throw:{error, atom} -> ok
end.
deepwcatch_complex(Level) ->
try
{ok, _Res} = deepfoothrow_cplx(Level)
catch
throw:{error, Res} -> Res
end.
%%------------------------------------------------------------------------------
foo(Arg) -> {error, Arg}.
foothrow(Arg) -> throw({error, Arg}).
deepfoo(Arg, 0) -> {error, Arg};
deepfoo(Arg, Level) -> deepfoo(Arg, Level - 1).
deepfoothrow(Arg, 0) -> throw({error, Arg});
deepfoothrow(Arg, Level) -> deepfoothrow(Arg, Level - 1).
foo_cplx() -> {error, {<<"Some">>, "Complex", data}}.
foothrow_cplx() -> throw({error, {<<"Some">>, "Complex", data}}).
deepfoo_cplx(0) -> {error, {<<"Some">>, "Complex", data}};
deepfoo_cplx(Level) -> deepfoo_cplx(Level - 1).
deepfoothrow_cplx(0) -> throw({error, {<<"Some">>, "Complex", data}});
deepfoothrow_cplx(Level) -> deepfoothrow_cplx(Level - 1).

Resources