Spec-Zone.ru › Haskell 9

6.6.6. Наследование любого другого класса

DeriveAnyClass
Since:

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 заполняется автоматически.

Обратите внимание на следующие детали

  • В случае, если вы пытаетесь вывести некоторый класс на новом типе, и 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
    

    Это эквивалентно тому, что

    instance Sizeable Bar where {}
    

    и затем будут применяться обычные правила для заполнения связанных типов по умолчанию, делая Size Bar равным Int.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.12.1/docs/users_guide/exts/derive_any_class.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API