-
DeriveAnyClass -
- Начиная с:
-
7.10.1
Разрешает использовать любой класс типов в предложениях
deriving.
С помощью DeriveAnyClass можно вывести любой другой класс. Компилятор просто сгенерирует объявление экземпляра без явно определённых методов. Это особенно полезно для классов, у которых минимальный набор пуст, и в частности при написании обобщённых функций.
Рассмотрим, например, простой класс для красивой печати SPretty, который выводит красиво отформатированные строки:
{-# LANGUAGE DefaultSignatures, DeriveAnyClass #-}
class SPretty a where
sPpr :: a -> String
default sPpr :: Show a => a -> String
sPpr = show
Если пользователь не предоставит реализацию sPpr вручную, то по умолчанию будет использоваться show. Теперь мы можем воспользоваться расширением DeriveAnyClass, чтобы легко реализовать экземпляр SPretty для нового типа данных:
data Foo = Foo deriving (Show, SPretty)
Приведённый выше код эквивалентен следующему:
data Foo = Foo deriving Show instance SPretty Foo
Иными словами, будет создан экземпляр SPretty Foo с пустыми реализациями всех методов. Поскольку в этом примере мы используем DefaultSignatures, реализация sPpr по умолчанию подставляется автоматически.
Обратите внимание на следующие детали
- Если вы попытаетесь вывести какой-либо класс для newtype и при этом включено расширение
GeneralizedNewtypeDeriving, приоритет будет уDeriveAnyClass. -
Контекст экземпляра определяется сигнатурами типов методов выводимого класса. Например, если класс имеет вид:
class Foo a where bar :: a -> String default bar :: Show a => a -> String bar = show baz :: a -> a -> Bool default baz :: Ord a => a -> a -> Bool baz x y = compare x y == EQ
и вы попытаетесь вывести его с помощью
DeriveAnyClass:instance Eq a => Eq (Option a) where ... instance Ord a => Ord (Option a) where ... instance Show a => Show (Option a) where ... data Option a = None | Some a deriving Foo
то выведенный экземпляр
Fooбудет иметь вид:instance (Show a, Ord a) => Foo (Option a)
Поскольку сигнатуры типов по умолчанию для
barиbazтребуют ограниченийShow aиOrd aсоответственно.Ограничения в сигнатурах типов не по умолчанию также могут влиять на вывод контекста экземпляра. Например, пусть у вас есть такой класс:
class HigherEq f where (==#) :: f a -> f a -> Bool default (==#) :: Eq (f a) => f a -> f a -> Bool x ==# y = (x == y)
и вы попробовали вывести для него экземпляр:
instance Eq a => Eq (Option a) where ... data Option a = None | Some a deriving HigherEq
Тогда возникнет ошибка примерно следующего содержания:
No instance for (Eq a) arising from the 'deriving' clause of a data type declarationЭто происходит потому, что для сигнатуры типа по умолчанию для
(==#)требуется экземплярEq (Option a), который, в свою очередь, требует экземплярEq a, отсутствующий в текущей области видимости. Но если немного изменить определениеHigherEq:class HigherEq f where (==#) :: Eq a => f a -> f a -> Bool default (==#) :: Eq (f a) => f a -> f a -> Bool x ==# y = (x == y)
то вывести экземпляр
HigherEq Optionуже можно. Обратите внимание, что единственное отличие состоит в том, что теперь сигнатура типа не по умолчанию для(==#)добавляет ограничениеEq a. Ограничения из сигнатур типов не по умолчанию никогда не попадают непосредственно в контекст выводимого экземпляра, но могут выполнять требования, предъявляемые сигнатурами типов по умолчанию. В приведённом выше примере сигнатура типа по умолчанию требовала экземплярEq a, и сигнатура не по умолчанию удовлетворила это требование, поэтому выведенный экземпляр имеет простой вид:instance HigherEq Option
-
DeriveAnyClassможно использовать с частично применёнными классами, например:data T a = MKT a deriving( D Int )
что генерирует
instance D Int a => D Int (T a) where {} -
DeriveAnyClassможно использовать для заполнения экземпляров по умолчанию для связанных семейств типов:{-# LANGUAGE DeriveAnyClass, TypeFamilies #-} class Sizable a where type Size a type Size a = Int data Bar = Bar deriving Sizable doubleBarSize :: Size Bar -> Size Bar doubleBarSize s = 2*sВыражение
deriving( Sizable )эквивалентно следующему:instance Sizeable Bar where {}после чего применяются обычные правила подстановки связанных типов из значения по умолчанию, в результате чего
Size Barстановится равнымInt.