Erlang polymorphism: multiple implementations of the same contract

please advice: what is the correct way for Erlang to have separate implementations from the contract and how to switch between them?

Thanks for the tips! Yf

+4
source share
5 answers

While others mention a behavior function, this is just a small helper to make sure that you implement all the functions in the module for the callback structure. If you have two implementations, aand b, and both implement the same functions, you can simply statically replace awith bin the calling module. For a static configuration where you have the best implementation, this is preferable.

,

 Mod = a,
 Mod:f(Args).

Mod . , . , .

+1

qlc table. . M:table/1,2 ets, dets, mnesia . ets:table(ets:new(foo, [set])). , , qlc.

+2

Erlang , ( when … ->) - .

:

len (T) when is_tuple(T) -> size(T);
len (L) when is_list(L) -> length(L).
+1

Perhaps look at the concept of behavior . At least for me there is little resemblance to OOP in terms of defining an interface and several implementation modules.

+1
source

If I understood your question, here is an example for an approach, it helps a lot for me. This approach helps to separate the interface and implementation.

"Interface".

-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.

An example implementation (for example, a 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.

Hope this helps.

0
source

Source: https://habr.com/ru/post/1531065/


All Articles