Ru/GHC/Class Instance Extensions
-XConstrainedClassMethods[edit]
Позволяет определять контекст над переменными типа из объявления класса в объявлениях методов. Частичная задача -XFlexibleContexts.
Пример
{-# LANGUAGE MultiParamTypeClasses, ConstrainedClassMethods #-}
class Map m k v where
empty :: m k v
put :: k -> v -> m k v -> m k v
get :: Eq k => k -> m k v -> Maybe v
Здесь в get определён контекст для k.
-XUndecidableInstances[edit]
Аналог синонимов типов. Синонимы класса. Применяется совместно с FlexibleInstances.
Пример
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class ObservableEq a where
(===) :: a -> a -> Bool
instance Eq a => ObservableEq a where
(===) = (==)
Экземпляр класса здесь представляет не конкретный тип, а (Eq a) => a.
-XOverlappingInstances[edit]
С предыдущей опцией можно наворотить кучу инстансов, которые могут быть применимы к одному и тому же набору типов.
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
class ObservableEq a where
(===) :: a -> a -> Bool
instance Eq a => ObservableEq a where
(===) = (==)
instance ObservableEq [a] where
(x:xs) === (y:ys) = True
_ === _ = False
И вот мы применяем (===) к [Int]. Какой инстанс сработает?
При включении флага OverlappingInstances GHC не будет ругаться, а выберет более точный инстанс (в нашем случае последний).
-XIncoherentInstances[edit]
Пойдём дальше, предположим, что некой функции мы выбрали наиболее точный инстанс, однако эта функция может примениться к типу, для которого подходит ещё более точный инстанс. Код:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances, IncoherentInstances #-}
class ObservableEq a where
(===) :: a -> a -> Bool
instance Eq a => ObservableEq a where
(===) = (==)
instance ObservableEq [a] where
(===) = undefined
instance ObservableEq [Integer] where
(===) = (==)
tailsObsEq xs ys = tail xs === tail ys
Здесь тип tailsObsEq :: (ObservableEq [a]) => [a] -> [a] -> Bool, т.е. был применён второй instance. Если мы выключим ключик IncoherentInstances, то поимеем ошибку при сравнении tailsObsEq [1] [1], т.к. это уже тип [Integer]. IncoherentInstances же закрепляет за tailsObsEq навсегда второй инстанс.