Ответ 1
Один из принципов, который пытается использовать язык haskell, заключается в добавлении дополнительных методов/классов или экземпляров в данном модуле, не должен вызывать каких-либо других модулей, которые зависят от данного модуля, либо не скомпилировать, либо иметь другое поведение (до тех пор, пока зависимые модули используют явные списки импорта).
К сожалению, это нарушено с OverlappingInstances. Например:
Модуль A:
{-# LANGUAGE FlexibleInstances, OverlappingInstances, MultiParamTypeClasses, FunctionalDependencies #-}
module A (Test(..)) where
class Test a b c | a b -> c where
test :: a -> b -> c
instance Test String a String where
test str _ = str
Модуль B:
module B where
import A (Test(test))
someFunc :: String -> Int -> String
someFunc = test
shouldEqualHello = someFunc "hello" 4
shouldEqualHello
выполняет равенство "привет" в модуле B.
Теперь добавьте следующее объявление экземпляра в A:
instance Test String Int String where
test s i = concat $ replicate i s
Было бы предпочтительнее, если бы это не повлияло на модуль B. Он работал до этого добавления и должен работать после этого. К сожалению, это не так.
Модуль B все еще компилируется, но теперь shouldEqualHello
теперь равно "hellohellohellohello"
. Поведение изменилось, хотя ни один из методов, которые он изначально не использовал, изменился.
Что еще хуже, нет способа вернуться к старому поведению, так как вы не можете выбрать, чтобы не импортировать экземпляр из модуля. Как вы можете себе представить, это очень плохо для обратной совместимости, так как вы не можете безопасно добавлять новые экземпляры в класс, который использует перекрывающиеся области, поскольку это может изменить поведение кода, использующего модуль (особенно это верно, если вы пишете библиотечный код). Это хуже, чем ошибка компиляции, так как это может быть очень сложно отследить изменение.
Единственное безопасное время для использования перекрывающихся экземпляров, на мой взгляд, - это когда вы пишете класс, который, как вы знаете, никогда не потребует дополнительных экземпляров. Это может произойти, если вы делаете код на основе сложного типа.