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). Это потому, что контексты не используются при «разрешении» вызова функции? И если да, то почему?

Заранее спасибо!


person Didier Pieroux    schedule 06.01.2017    source источник


Ответы (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)

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

person shree.pat18    schedule 06.01.2017
comment
Большое спасибо, Шри. По сути, похоже, что GHC работает именно так с этим (нестандартным) расширением. Закрываю вопрос. - person Didier Pieroux; 07.01.2017