从记录中的字段派生实例

All*_*n W 5 haskell deriving

示例代码:

{-# LANGUAGE NamedFieldPuns #-}

module Sample where

class Sample a where
  isA :: a -> Bool
  isB :: a -> Bool
  isC :: a -> Bool

data X =
  X

instance Sample X where
  isA = undefined
  isB = undefined
  isC = undefined

data Wrapper = Wrapper
  { x :: X
  , i :: Int
  }

instance Sample Wrapper where
  isA Wrapper {x} = isA x
  isB Wrapper {x} = isB x
  isC Wrapper {x} = isC x
Run Code Online (Sandbox Code Playgroud)

在这里,我有一个由实现的类,X然后是另一个Wrapper包含的记录X

我想通过实例字段Wrapper来导出Sample实例x

我知道我可以通过获取该字段并为每个函数自己调用而做到这一点,如图所示。

是否有某种标志或某种方法可以自动执行或仅执行一次?

这似乎与DerivingVia和相似GeneralisedNewtypeDeriving,但两者似乎newtype都只针对或强制类型

Kot*_*lar 4

以下是一些不需要任何扩展的策略,但会牺牲一些前期成本来轻松派生这些类。

请注意,由于Sample不是新类型,因此不能保证它只能容纳一个X而不是两个、更多或可变数量(Maybe X??Either X X)。X因此,正如您将看到的,您的选项必须明确结构内部的选择,这可能是自动派生此结构的扩展存在的可能原因。

导出一个函数而不是多个函数

为了满足Sample,我们确实需要一个X. 让我们将其设为类型类:

class HasX t where
  getX :: t -> X

class Sample t where
  isA :: t -> Bool
  isB :: t -> Bool
  isC :: t -> Bool
  default isA :: HasX t => t -> Bool
  isA = isA . getX
  default isB :: HasX t => t -> Bool
  isB = isB . getX
  default isC :: HasX t => t -> Bool
  isC = isC . getX

instance HasX Wrapper where
  getX = x

instance Sample Wrapper -- no implementation necessary
Run Code Online (Sandbox Code Playgroud)

通过泛型派生

假设我们只想处理X第一个字段的记录。为了匹配类型结构,我们可以使用GHC.GenericsHasX这里我们添加了一种默认第一个字段的方法:

class HasX t where
  getX :: t -> X
  default getX :: (Generic a, HasX (Rep a)) => t -> X
  getX = getX . from

instance HasX (M1 D d (M1 C c (M1 S s (Rec0 X) :*: ff))) o where
  getX (M1 (M1 ((M1 (K1 x)) :*: _))) = x
Run Code Online (Sandbox Code Playgroud)

的最后一个实例与具有单个构造函数 ( ) 的HasX任何记录 ( ) 匹配,该构造函数具有多个 ( ) 字段 ( ),第一个字段的类型为 ( ) 。M1 DM1 C:*:M1 SRec0X

(是的,通用实例很笨重。欢迎编辑。)

(要查看 泛型类型的准确表示Wrapper,请Rep Wrapper在 GHCi 控制台中检查。)

现在 的实例Wrapper可以写成:

data Wrapper = Wrapper
  { x :: X
  , i :: Int
  }
  deriving (Generic, HasX, Sample)
Run Code Online (Sandbox Code Playgroud)