Дублирование реализации протокола в зависимых проектах

У меня проблемы с консолидацией реализации протокола в моем проекте Elixir. Чтобы быть более конкретным, я использую Ecto и какой-то простой проект под названием Gold (не имеет большого значения). Проблема в том, что оба они (Ecto и Gold) используют 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 в качестве зависимости, но это кажется немного излишним, чтобы просто реализовать один такой протокол. Нет ли какого-нибудь макроса, чтобы проверить, был ли модуль уже реализован?


person Kelu Thatsall    schedule 05.07.2016    source источник
comment
Существует Protocol.assert_impl!(имя_модуля_реализации, имя_протокола), но я не уверен, будет ли он работать во время компиляции (например, до определения десятичной реализации gold).   -  person Qqwy    schedule 06.07.2016
comment
Проблема с утверждениями заключается в том, что они выдают неудачный результат, поэтому я действительно не смог бы проверить с ними какие-либо отрицательные результаты... И если бы я смог, то что, если сначала загрузится модуль с этой проверкой, а затем другой без проверки - та же проблема повторится!   -  person Kelu Thatsall    schedule 06.07.2016
comment
IMO, эта реализация протокола должна происходить в общем пакете (либо в самом decimal, либо в другом пакете с именем decimal_poison). Было бы хорошо, если бы вы открыли тему на Ecto, чтобы осветить эту проблему.   -  person Derek Kraan    schedule 03.01.2019


Ответы (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

Это немного непривычно, но тогда вам не нужно добавлять Экто, а просто проверьте, не втянуло ли его что-то еще.

person sdc    schedule 17.08.2020