依赖项目中的重复协议实现

12

我在我的 Elixir 项目中遇到了协议实现的整合问题。更具体地说,我使用了 Ecto 和一些简单的项目叫做 Gold(目前不太重要)。问题是,它们两个(EctoGold)都使用 Poison 来序列化 Decimals(并实现适当的协议)。

Ecto 的实现看起来有点像这样:

defimpl Poison.Encoder, for: Decimal do
    def encode(decimal, _opts), do: <<?", Decimal.to_string(decimal)::binary, ?">>
end

在开发过程中,会出现一个警告,提示模块被复制了:

warning: redefining module Poison.Encoder.Decimal (current version loaded from /(...)/_build/dev/lib/gold/ebin/Elixir.Poison.Encoder.Decimal.beam)
  lib/ecto/poison.ex:2

但是当我尝试使用例如exrm来构建一个发布版本时,我会得到一个错误提示,说我有duplicate_modules

===> Provider (release) failed with: {error,
                     {rlx_prv_assembler,
                      {release_script_generation_error,
                       systools_make,
                       {duplicate_modules,
                        [{{'Elixir.Poison.Encoder.Decimal',
                           gold,
                           "/(...)/rel/bitcoin_api/lib/gold-0.12.0/ebin"},
                          {'Elixir.Poison.Encoder.Decimal',
                           ecto,
                           "/(...)/rel/bitcoin_api/lib/ecto-2.0.2/ebin"}}]}}}}

我该如何处理这个问题? 这里的情况是我实际上使用自己的版本 Gold,所以我可以篡改它来尽快修复它。 我知道我可以将Ecto作为依赖项添加到Gold中,但仅为了实现一个协议,这似乎有点过度。 是否有某种宏可用于检查模块是否已经被实现?


有 Protocol.assert_impl!(implementation_module_name, protocol_name) 这个函数,但我不确定它是否能在编译时工作(例如,在 gold 的 Decimal 实现被定义之前)。 - Qqwy
断言的问题是,当结果不成功时会抛出异常,因此我实际上不能使用它们来检查任何负面结果...即使我能够使用,如果带有此检查的模块首先被加载,而没有检查的另一个模块后加载,则相同的问题将再次发生! - Kelu Thatsall
在我看来,这个协议实现应该在一个公共包中进行(可以是decimal本身,也可以是另一个名为decimal_poison的包)。如果您在Ecto上开一个问题以突出这个问题,那就太好了。 - Derek Kraan
1个回答

1
一个快速的解决方法可能是在Gold的实现中包装一个Code.ensure_loaded?/1
unless Code.ensure_loaded?(Ecto) do
  defimpl Poison.Encoder, for: Decimal do
    def encode(decimal, _opts), do: <<?", Decimal.to_string(decimal)::binary, ?">>
  end
end

虽然有点麻烦,但你不需要添加 Ecto,只需检查是否已有其他东西引入它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接