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?

Kelu Thatsall
  • 2,494
  • 1
  • 22
  • 50
  • 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. – Qqwy Jul 06 '16 at 07:04
  • 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! – Kelu Thatsall Jul 06 '16 at 07:21
  • 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. – Derek Kraan Jan 03 '19 at 10:51

1 Answers1

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

sdc
  • 2,603
  • 1
  • 27
  • 40