Помогите интерпретировать сообщение об ошибке перекрывающихся экземпляров - PullRequest
4 голосов
/ 25 июля 2011

Я нахожусь в тупике с этим сообщением об ошибке перекрывающихся экземпляров. Извините, это нетривиальный проект, но ошибка должна быть локальной для сигнатур типов.

Сначала я объявляю f определенного типа,

let f = undefined :: (CompNode Int)

Затем я пытаюсь вызвать на нем свою функцию pshow :: PrettyShow a => a -> String. Я получаю это сообщение об ошибке.

> pshow f

<interactive>:1:1:
    Overlapping instances for PrettyShow (CompNode Int)
      arising from a use of `pshow'
    Matching instances:
      instance (G.Graph g, PrettyShow (G.Vertex g)) => PrettyShow g
        -- Defined at Graph.hs:61:10-57
      instance (PrettyShow a, Show a) => PrettyShow (CompNode a)
        -- Defined at Interpreter.hs:61:10-58

Проблема в том, что CompNode Int не является графиком, поэтому я не думаю, что первый соответствующий экземпляр должен запускаться. (Второй - тот, который я хочу выполнить.) Действительно, если я напишу функцию, для которой аргумент должен быть графом,

> :{
| let g :: G.Graph a => a -> a
|     g = id
| :}

и затем вызвать его на f , я получаю ожидаемое сообщение об отсутствии экземпляра,

> g f

<interactive>:1:1:
    No instance for (G.Graph (CompNode Int))

Заранее спасибо, извините за краудсорсинг. Я использую GHC 7.0.4.

1 Ответ

4 голосов
/ 25 июля 2011

Проблема в том, что CompNode Int не является графиком, поэтому я не думаю, что первый соответствующий экземпляр должен запускаться.

Вы могли бы подумать об этом, но, к сожалению, это не сработало.

Когда GHC выбирает экземпляр, он смотрит только на head , т.е. на часть после имени класса. Только после выбора экземпляра он проверяет контекст , то есть часть перед =>. Несоответствия в контексте могут вызвать отклонение экземпляра и привести к ошибке проверки типа, но они не приведут к тому, что GHC будет возвращаться и искать другой экземпляр.

Итак, учитывая эти случаи:

instance (G.Graph g, PrettyShow (G.Vertex g)) => PrettyShow g

instance (PrettyShow a, Show a) => PrettyShow (CompNode a)

... если мы игнорируем контекст, они выглядят так:

instance PrettyShow g

instance PrettyShow (CompNode a)

Что должно прояснить, что первый экземпляр является полностью общим и перекрывает абсолютно все.

В некоторых случаях вы можете использовать расширение OverlappingInstances, но это не меняет вышеуказанное поведение; скорее, это позволяет GHC разрешать неоднозначные случаи, выбирая уникально наиболее конкретный случай, если таковой существует. Но использование перекрывающихся экземпляров может быть сложным и привести к загадочным ошибкам, поэтому я рекомендую вам сначала переосмыслить дизайн и посмотреть, сможете ли вы полностью избежать этой проблемы.

Тем не менее, в данном конкретном примере CompNode a действительно является однозначно более конкретным совпадением для CompNode Int, поэтому GHC выберет его вместо общего экземпляра.

...