Spec-Zone.ru › Haskell 9

6.6.8. Выведение через

DerivingVia
Подразумевает:

DerivingStrategies

Начиная с:

8.6.1

Это позволяет deriving экземпляр класса для типа, указав другой тип, который уже является экземпляром этого класса. Это имеет смысл, только если методы имеют идентичные представления во время выполнения, то есть coerce (см. ограничение Coercible) может преобразовать существующую реализацию в требуемую. В противном случае сгенерированный код будет отклонён с ошибкой типов.

DerivingVia обозначается использованием стратегии выведения via. Для via требуется указать другой тип (тип via), через который будет выполняться coerce. Например, этот код:

{-# LANGUAGE DerivingVia #-}

import Numeric

newtype Hex a = Hex a

instance (Integral a, Show a) => Show (Hex a) where
  show (Hex a) = "0x" ++ showHex a ""

newtype Unicode = U Int
  deriving Show
    via (Hex Int)

-- >>> euroSign
-- 0x20ac
euroSign :: Unicode
euroSign = U 0x20ac

Генерирует следующий экземпляр

instance Show Unicode where
  show :: Unicode -> String
  show = Data.Coerce.coerce
    @(Hex Int -> String)
    @(Unicode -> String)
    show

Это расширение обобщает GeneralizedNewtypeDeriving. Чтобы вывести Num Unicode с помощью GND (deriving newtype Num), необходимо повторно использовать экземпляр Num Int. С помощью DerivingVia мы можем явно указать тип представления Int:

newtype Unicode = U Int
  deriving Num
    via Int

  deriving Show
    via (Hex Int)

euroSign :: Unicode
euroSign = 0x20ac

В объявлениях экземпляров часто дублируется код. Распространённый шаблон — подъём операций над функтором Applicative. Вместо всеохватывающих экземпляров для f a, которые перекрываются со всеми другими подобными экземплярами, как здесь:

instance (Applicative f, Semigroup a) => Semigroup (f a) ..
instance (Applicative f, Monoid    a) => Monoid    (f a) ..

Вместо этого мы можем создать newtype App (где App f a и f a представлены в памяти одинаково) и использовать DerivingVia, чтобы явно задействовать этот шаблон:

{-# LANGUAGE DerivingVia, DeriveFunctor, GeneralizedNewtypeDeriving #-}

import Control.Applicative

newtype App f a = App (f a) deriving newtype (Functor, Applicative)

instance (Applicative f, Semigroup a) => Semigroup (App f a) where
  (<>) = liftA2 (<>)

instance (Applicative f, Monoid a) => Monoid (App f a) where
  mempty = pure mempty

data Pair a = MkPair a a
  deriving stock
    Functor

  deriving (Semigroup, Monoid)
    via (App Pair a)

instance Applicative Pair where
  pure a = MkPair a a

  MkPair f g <*> MkPair a b = MkPair (f a) (g b)

Обратите внимание, что тип via не обязательно должен быть newtype. Единственное ограничение состоит в том, что с исходным типом данных для него должно быть возможно приведение. Это означает, что newtype могут быть вложены произвольным образом, как в следующем примере:

newtype Kleisli m a b = Kleisli (a -> m b)
  deriving (Semigroup, Monoid)
    via (a -> App m b)

Здесь мы используем экземпляр Monoid ((->) a).

При использовании совместно с StandaloneDeriving меняется порядок: сначала указывается экземпляр, на основе которого выполняется выведение, а затем определяемый экземпляр. Например:

deriving via (a -> App m b) instance Monoid (Kleisli m a b)

© 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.14.1/docs/users_guide/exts/deriving_via.html

Spec-Zone.ru

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