Duplicate protocol implementations in dependent projects
Asked Answered
T

1

12

I have problems with consolidation of protocol implementation in my Elixir project. To be more specific I use Ecto and some simple project called Gold (doesn't matter much atm). The problem is, both of them (Ecto and Gold) use Poison to serialize Decimals (and implement proper Protocol).

Implementation for Ecto looks somewhat like this:

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

During development there is a warning saying that the module is duplicated:

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

But when I try to use for instance exrm to build a release, then I get errors saying that I have 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"}}]}}}}

How should I deal with this? The case here is I actually use my own version of Gold, so I can tamper with it to fix it asap. I know I can just add Ecto to Gold as a dependency, but that seems a bit overkill to just implement one Protocol like this. Isn't there some kind of a macro to check if a module has already been implemented?

Titograd answered 5/7, 2016 at 21:30 Comment(3)
There is Protocol.assert_impl!(implementation_module_name, protocol_name), but I am not sure if it will work at compile time (e.g. before gold's Decimal implementation is defined.Leasehold
The problem with asserts is they throw on unsuccesful result, so I wouldn't really be able to check any negative results with them... And event if I was able to then what if the module with this check gets loaded first and then the other one without check - the same issue would happen again!Titograd
IMO this protocol implementation should be happening in a common package (either in decimal itself, or in another package called decimal_poison). It would be good if you opened an issue on Ecto to highlight this issue.Scribble
D
1

A quick fix could be to wrap Gold's implementation in a 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

Its a little icky but then you don't have to add Ecto, but just check if something else already pulled it in

Dr answered 17/8, 2020 at 13:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.