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)

Тогда не было бы совпадений, потому что кортеж не является списком.

Другие вопросы по тегам