Haskell/GHC: сообщается о перекрывающихся экземплярах, в то время как контекст допускает только один
Уважаемые эксперты Haskell/GHC,
Я не очень понимаю, почему GHC сообщает о перекрывающихся экземплярах, в то время как только один действительно действителен в соответствии с предоставленными контекстами. Например, давайте рассмотрим следующий фрагмент кода:
{-# LANGUAGE FlexibleInstances #-}
class C a where
foo :: a -> String
foo x = "OK"
instance C Bool
instance (C a) => C [a]
instance (C a) => C [(Char, a)]
main = print $ foo [('a', True)]
Компиляция дает:
Test.hs:13:16: error:
* Overlapping instances for C [(Char, Bool)]
arising from a use of `foo'
Matching instances:
instance C a => C [a] -- Defined at Test.hs:9:10
instance C a => C [(Char, a)] -- Defined at Test.hs:11:10
* In the second argument of `($)', namely `foo [('a', True)]'
In the expression: print $ foo [('a', True)]
In an equation for `main': main = print $ foo [('a', True)]
Дело в том, что ('a', True)
имеет тип (Char, Bool)
который не является примером C
, Следовательно, instance C a => C [a]
не относится к стоимости [('a', True)]
,
Поэтому, почему GHC учитывает это?
Вопрос действительно в том, чтобы понять поведение GHC, а не в том, как избежать проблемы (например, используя OverlappingInstances
). Это потому, что контексты не используются при "разрешении" вызова функции? И если так, то почему?
Заранее спасибо!
1 ответ
Мое понимание (может быть очень неправильно):
Во-первых, из документации:
При сопоставлении GHC не учитывает контекст объявления экземпляра (context1 и т. Д.). Поведение GHC по умолчанию состоит в том, что ровно один экземпляр должен соответствовать ограничению, которое он пытается разрешить. Вполне возможно, что существует вероятность совпадения (включая, скажем, оба объявления (A) и (B)); ошибка сообщается только в том случае, если конкретное ограничение соответствует более чем одному.
Флаг -XOverlappingInstances инструктирует GHC разрешить совпадение более чем одному экземпляру, при условии, что есть наиболее конкретный.
В вашем случае тип передается foo
является [(Char,Bool)]
, Это удовлетворяет общим [a]
и более специализированный [(Char,a)]
, В отсутствии OverlappingInstances
флаг, наиболее конкретный сценарий соответствия не применяется, и сообщается об ошибке.
Теперь, если вам нужно немного подправить код, сделайте так:
instance C Bool
instance (C a) => C [a]
instance (C a) => C (Char, a)
Тогда не было бы совпадений, потому что кортеж не является списком.