lomeo: (лямбда)
Dmitry Antonyuk ([personal profile] lomeo) wrote2008-07-29 03:54 pm

Расширения классов типов. Конспект.

Расширения типа fundep или MPTC обычно понятны бывают сразу, ну, по крайней мере, их идея. А вот чтобы разобраться с ошибками при инстанциировании класса, придётся разобраться.


-XConstrainedClassMethods
Позволяет определять контекст над переменными типа из объявления класса в объявлениях методов.
Частичная задача -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
Аналог синонимов типов. Синонимы класса. Применяется совместно с FlexibleInstances.

Пример
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

class ObservableEq a where
    (===) :: a -> a -> Bool

instance Eq a => ObservableEq a where
    (===) = (==)


Экземпляр класса здесь представляет не конкретный тип, а (Eq a) => a.

-XOverlappingInstances
С предыдущей опцией можно наворотить кучу инстансов, которые могут быть применимы к одному и тому же набору типов.

{-# 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
Пойдём дальше, предположим, что некой функции мы выбрали наиболее точный инстанс, однако эта функция может примениться к типу, для которого подходит ещё более точный инстанс. Код:

{-# 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 навсегда второй инстанс.

На сегодня всё :-) Более общие вещи типа FlexibleInstance ещё до тонкостей не понимаю.

[identity profile] nivanych.livejournal.com 2008-07-30 05:37 am (UTC)(link)
Афтар тоже сомневается.
Но до сих пор не убрал этот suggestion.
И я тоже сомневаюсь, поэтому и спрашиваю :-)