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) ..

Мы можем вместо этого создать новый тип 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 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.12.1/docs/users_guide/exts/deriving_via.html

Spec-Zone.ru

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