Проблема возникает, когда у нас уже установлены пакеты B и C, но они созданы для разных версий D, а затем мы пытаемся использовать оба пакета B и C вместе в пакете A: проблема зависимости от Diamond Это может работать нормально, но только если пакеты Bи C не предоставляют типы, определенные в D, в своих интерфейсах.Если они это сделают, пакет A не сможет использовать функции из B и C вместе, потому что они не будут работать с одним и тем же типом.То есть вы получите ошибку типа.
Чтобы выбрать конкретный пример, предположим, что пакет D является строкой тестирования, и у нас установлены обе версии: bytestring-0.9.0.1 и 0.9.0.4.Допустим, B - это utf8-строка, а C - это регулярное выражение.Допустим, пакет A - это программа-редактор Yi.Итак, дело в том, что в каком-то месте кода в Yi мы хотим передать строку байтов, созданную в результате декодирования UTF-8, в качестве входных данных для одной из функций регулярного выражения.Но это не работает, потому что функции в пакете utf8-string используют тип ByteString из bytestring-0.9.0.1, в то время как функции регулярного выражения в пакете regex используют тип ByteString из bytestring-0.9.0.4.Таким образом, мы получаем ошибку типа, когда пытаемся скомпилировать Yi:
Не удалось найти ожидаемый тип bytestring-0.9.0.4:Data.ByteString.ByteString'
against inferred type
bytestring-0.9.0.1: Data.ByteString.ByteString '
Насколько GHCзнает, эти два типа совершенно не связаны!
Это явно очень раздражает.Там также нет простого решения.В этом примере мы предполагаем, что пакеты B и C уже были собраны, поэтому на самом деле нет никакого способа разумно использовать два пакета вместе, не перестраивая их для другой версии пакета D. В этом случае очевидным решением являетсявосстановить B, чтобы использовать D-1.1, а не D-1.0.Проблема с перестройкой пакета, конечно, в том, что он ломает все остальные пакеты, которые уже были скомпилированы.Не ясно, что вы хотите, чтобы менеджер пакетов автоматически перестраивал множество явно не связанных пакетов.
В долгосрочной перспективе лучшим решением, по-видимому, было бы то, что делает Nix.В приведенном выше примере вместо замены пакета B, созданного для D-1.0, на B, созданного для D-1.1, Nix добавит еще один экземпляр B, созданный для D-1.1.Таким образом, оригинальный экземпляр B останется неизменным, и ничто не сломается.Это функциональный подход: мы никогда не изменяем значения (установленные пакеты), мы просто создаем новые и собираем старые, когда они больше не нужны.
На практике это означает, что мы должны идентифицировать установленные пакеты, используя некоторый хэшпакет и хэши всех зависимых пакетов.jhc уже делает это, и есть шаги, чтобы сделать нечто подобное для GHC, хотя и нацелены больше на отслеживание изменений API / ABI.Для разумного управления пакетами на основе исходного кода, я думаю, это правильное направление.
Я должен отметить, что это не новая проблема.Вы смогли создать эту проблему с тех пор, как ghc начал разрешать установку нескольких версий одного и того же пакета одновременно.Мы просто замечаем это гораздо чаще, потому что мы разделяем базовый пакет и позволяем обновлять эти разделенные пакеты.
В настоящее время Cabal предупреждает об этой проблеме, но недействительно поможет вам решить это.Для приведенного выше примера мы получили бы:
$ cabal configure
Configuring A-1.0...
Warning: This package indirectly depends on multiple versions of
the same package. This is highly likely to cause a compile failure.
package B-1.0 requires D-1.0
package C-1.0 requires D-1.1