【问题标题】:Erlang polymorphism: multiple implementations of the same contract?Erlang 多态性:同一个合约的多个实现?
【发布时间】:2025-11-25 02:30:01
【问题描述】:

什么是正确的 Erlang 方法将实现与合同分开以及如何在它们之间切换?

【问题讨论】:

    标签: erlang polymorphism


    【解决方案1】:

    虽然其他人提到了 behaviour 功能,但它只是一个小帮手,可确保您在一个模块中为回调结构实现所有功能。如果您有两个实现,ab,并且都实现了相同的功能,您可以在调用模块中将a 静态替换为b。对于具有更好实现的静态配置,这是更可取的。

    如果问题具有更动态的性质,您可以这样做

     Mod = a,
     Mod:f(Args).
    

    然后适当地在代码集Mod 中。这使您可以在程序运行时动态控制要调用的模块。不完全清楚你想要这两者中的哪一个。

    【讨论】:

    • 是的 - 在你评论的前半部分是我想要的 - 所以现在我确定了 - 但你能否详细说明第二部分 - 你所说的“更动态”是什么意思自然',因为我不明白如何在运行时由于单一分配而在一次绑定后重新绑定 Mod...只是没有得到它我想...谢谢!
    • 假设Mod 作为函数的参数给出。现在调用Mod:f(Args) 的代码是动态的,因为我们可以改变参数以获得不同的程序行为。通常,您将Mod 推送到#state{ implementation = Mod, ...} 记录中,然后使用它来获取动态调用。请注意:您不能在代码库中grep(1),并且透析器很难弄清楚调用结构。这些是您为这样的解决方案所做的牺牲。
    【解决方案2】:

    多态性的一个很好的例子是qlc 模块和结构table。在etsdetsmnesia 等中查看各种M:table/1,2 实现。例如,在 shell 中尝试 ets:table(ets:new(foo, [set])). 并查看 qlc 文档和示例。

    【讨论】:

      【解决方案3】:

      由于 Erlang 是动态类型的,function guardswhen … -> 位)是表达多态性的方式。

      例如:

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

      【讨论】:

      • 我正在寻找如何分离合同和实施的方法 - 例如:M1,M2 实施一些合同 C - 在 M1 中,C 使用一种技术实现,M2 使用其他技术......类似的东西......
      【解决方案4】:

      也许看看behaviours 概念。至少对我来说,在接口定义和多个实现模块方面与 OOP 有一点相似之处。

      【讨论】:

      • 是的 - 谢谢 - 我在这里看到了一些东西 (*.com/questions/4119477/…) 但我只是不确定行为是否可以用于此目的 - 我同意不将 OOP 映射到 Erlang 结构 - 但是我发现这种分离的要求是合法的,所以试图弄清楚如何正确地做到这一点......无论如何感谢提示!
      【解决方案5】:

      如果我意识到你的问题,这里是方法的例子,这对我来说非常有用。这种方法有助于分离接口和实现。

      “接口”模块。

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

      我希望这会有所帮助。

      【讨论】:

      • 非常感谢维亚切斯拉夫的努力!差不多就是这样 - 只是如何避免其中的状态(因为我真正想要避免的是在 erlang 中应用 OOP 原则) - 基本上我的想法只是强制实施以实现合同的功能并让其他一些机制来使用具体的模块 - 无论如何感谢你指出我的行为 - 正如我现在所看到的 - 我会将状态分成具体行为下的进程和函数 - 并让模式匹配和守卫处理多态性 - 我认为正确吗?感谢您的耐心等待!
      • 我不确定我得到了你想要的 :) 通常最好将一些状态传递给函数调用并返回新的状态作为返回结果的一部分。我认为没有理由避免它。当然,您可以在实现下方的单独进程中隐藏某些状态,但在这样做之前您应该三思而后行。如我所见,它会使您的代码难以测试和调试。
      • 在这种情况下,我的意思是“对象状态”——我在这个线程中的主要观点是考虑模块“设计”和代码/合同分离:) 无论如何——我现在有足够的线索来做什么考虑下一步 - 感谢您的提示!
      最近更新 更多