【发布时间】:2025-11-25 02:30:01
【问题描述】:
什么是正确的 Erlang 方法将实现与合同分开以及如何在它们之间切换?
【问题讨论】:
标签: erlang polymorphism
什么是正确的 Erlang 方法将实现与合同分开以及如何在它们之间切换?
【问题讨论】:
标签: erlang polymorphism
虽然其他人提到了 behaviour 功能,但它只是一个小帮手,可确保您在一个模块中为回调结构实现所有功能。如果您有两个实现,a 和 b,并且都实现了相同的功能,您可以在调用模块中将a 静态替换为b。对于具有更好实现的静态配置,这是更可取的。
如果问题具有更动态的性质,您可以这样做
Mod = a,
Mod:f(Args).
然后适当地在代码集Mod 中。这使您可以在程序运行时动态控制要调用的模块。不完全清楚你想要这两者中的哪一个。
【讨论】:
Mod 作为函数的参数给出。现在调用Mod:f(Args) 的代码是动态的,因为我们可以改变参数以获得不同的程序行为。通常,您将Mod 推送到#state{ implementation = Mod, ...} 记录中,然后使用它来获取动态调用。请注意:您不能在代码库中grep(1),并且透析器很难弄清楚调用结构。这些是您为这样的解决方案所做的牺牲。
多态性的一个很好的例子是qlc 模块和结构table。在ets、dets、mnesia 等中查看各种M:table/1,2 实现。例如,在 shell 中尝试 ets:table(ets:new(foo, [set])). 并查看 qlc 文档和示例。
【讨论】:
由于 Erlang 是动态类型的,function guards(when … -> 位)是表达多态性的方式。
例如:
len (T) when is_tuple(T) -> size(T);
len (L) when is_list(L) -> length(L).
【讨论】:
也许看看behaviours 概念。至少对我来说,在接口定义和多个实现模块方面与 OOP 有一点相似之处。
【讨论】:
如果我意识到你的问题,这里是方法的例子,这对我来说非常有用。这种方法有助于分离接口和实现。
“接口”模块。
-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.
一些实现示例(如派生类)。
-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}.
现在您可以执行以下操作:
usage_example() ->
{ok, State} = contract:new(foo, [{repeat_count, 15}]),
{ok, Result, NewState} = contract:do_something("bar", State),
ok.
我希望这会有所帮助。
【讨论】: