示例代码:
{-# 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在这里,我有一些由X实现的类,然后是另一个包含X的记录Wrapper。
我希望Wrapper通过其字段x派生Sample实例。
我知道我可以通过获取字段并为每个函数自己调用它来做到这一点,如所示。
是否有某种标志或方法来自动地或仅一次地这样做?
这似乎与DerivingVia和GeneralisedNewtypeDeriving相似,但两者似乎都只针对newtype或强制类型。
发布于 2019-05-03 11:08:55
以下是一些不需要任何扩展的策略,但是为了方便地派生这些类,需要预先花费一些费用。
请注意,由于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通过泛型派生
假设我们只想处理将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)) :*: _))) = xHasX的最后一个实例将任何记录(M1 D)与一个构造函数(M1 C)匹配,该构造函数具有多个(:*:)字段(M1 S),第一个字段为类型(Rec0) X。
(是的,泛型实例很难处理。编辑欢迎)
(要查看Wrapper的泛型类型的确切表示形式,请检查GHCi控制台中的Rep Wrapper。)
现在,可以将Wrapper的实例编写为:
data Wrapper = Wrapper
{ x :: X
, i :: Int
}
deriving (Generic, HasX, Sample)https://stackoverflow.com/questions/55964963
复制相似问题