Haskell Cabal: "пакет косвенно зависит от нескольких версий одного и того же пакета"

После очистки всех моих пакетов cabal install ed я запустил следующий сеанс:

$ cabal update
Downloading the latest package list from hackage.haskell.org
[email protected]:~/.cabal/packages$ cabal install cabal-dev
Resolving dependencies...
Downloading cabal-dev-0.9.1...
[1 of 1] Compiling Main             ( /tmp/cabal-dev-0.9.124882/cabal-dev-0.9.1/Setup.hs, /tmp/cabal-dev-0.9.124882/cabal-dev-0.9.1/dist/setup/Main.o )
Linking /tmp/cabal-dev-0.9.124882/cabal-dev-0.9.1/dist/setup/setup ...
Configuring cabal-dev-0.9.1...
Warning: This package indirectly depends on multiple versions of the same
package. This is highly likely to cause a compile failure.
package containers-0.4.2.1 requires array-0.4.0.0
package Cabal-1.14.0 requires array-0.4.0.0
package text-0.11.1.13 requires array-0.4.0.0
package deepseq-1.3.0.0 requires array-0.4.0.0
package containers-0.4.2.1 requires array-0.4.0.0
package HTTP-4000.2.2 requires array-0.4.0.0
package cabal-dev-0.9.1 requires containers-0.4.2.1
package Cabal-1.14.0 requires containers-0.4.2.1
package template-haskell-2.7.0.0 requires containers-0.4.2.1
Building cabal-dev-0.9.1...
Preprocessing executable 'ghc-pkg-6_8-compat' for cabal-dev-0.9.1...
<command line>: cannot satisfy -package-id Cabal-1.14.0-4af45d3c8d10dc27db38ae0e7e5a952b: 
    Cabal-1.14.0-4af45d3c8d10dc27db38ae0e7e5a952b is unusable due to missing or recursive dependencies:
      array-0.4.0.0-46f61f5fd9543ebf309552ef84dccc86 containers-0.4.2.1-98f9aa15f9c08b13673dc9d89385f449
    (use -v for more information)
cabal: Error: some packages failed to install:
cabal-dev-0.9.1 failed during the building phase. The exception was:
ExitFailure 1
$ 

Поэтому я не могу установить cabal-dev, по-видимому, либо, что

  • он "косвенно зависит от нескольких версий одного и того же пакета". Тем не менее, cabal не указывает пакет, который, как он утверждает, cabal-dev требует нескольких версий.
  • Cabal-1.14.0 имеет "отсутствующие или рекурсивные зависимости", в частности, с участием array-0.4.0.0 и containers-0.4.2.1.

График списков зависимостей, которые он перечисляет, подтверждает, что ни одно из этих утверждений не является истинным (или списки, которые он перечисляет, являются ложными или неполными):

claimed dependency graph of cabal-dev-0.9.1

Итак: что мне не хватает? Кто или что неверно: me, cabal или один или несколько пакетов?

Я запускаю:

$ cabal --version
cabal-install version 0.10.2
using version 1.10.1.0 of the Cabal library 
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.4.1
$

Ответы

Ответ 1

Проблема возникает, когда у нас уже установлены пакеты B и C, но они построены для разных версий D, а затем мы пытаемся использовать оба пакета B и C вместе в пакете A: Проблема диамантной зависимости Это может работать нормально, но только если пакеты B и C не отображают типы, определенные в D в своих интерфейсах. Если они это сделают, пакет A не сможет использовать функции из B и C вместе, потому что они не будут работать с одним и тем же типом. То есть вы получите ошибку типа.

Чтобы выбрать конкретный пример, предположим, что пакет D является байтовым, и мы установили как bytestring-0.9.0.1, так и 0.9.0.4. Допустим, что B - это utf8-строка, а C - регулярное выражение. Допустим, что пакет A - это программа Yi editor. Итак, дело в том, что в каком-то месте в коде в Yi мы хотим передать bytestring, созданный в результате декодирования UTF-8, в качестве входных данных для одной из функций регулярного выражения. Но это не работает, потому что функции в пакете utf8-string используют тип ByteString из bytestring-0.9.0.1, в то время как функции regex в пакете 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