示例代码:
{-# 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都只针对或强制类型
以下是一些不需要任何扩展的策略,但会牺牲一些前期成本来轻松派生这些类。
请注意,由于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.Generics。HasX这里我们添加了一种默认第一个字段的方法:
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)