Reputation: 1189
What's correct Erlang way to have separated implementations from the contract and how to switch between them?
Upvotes: 7
Views: 1223
Reputation: 26121
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.
Upvotes: 2
Reputation: 18879
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.
Upvotes: 4
Reputation: 1745
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.
Upvotes: 0
Reputation: 4571
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.
Upvotes: 1
Reputation: 2496
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).
Upvotes: 1