从记录中的字段派生实例
Derive instance from field in record
示例代码:
{-# 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
在这里,我有一些 class 是由 X
实现的,然后是另一个包含 X
.
的记录 Wrapper
我希望 Wrapper
通过其字段 x
派生 Sample
实例。
我知道我可以通过获取字段并自己为每个函数调用它来做到这一点,如图所示。
是否有一些标记或方法可以自动或仅执行一次?
这似乎类似于 DerivingVia
和 GeneralisedNewtypeDeriving
,但两者似乎都只针对 newtype
或可强制类型
这里有一些不需要任何扩展的策略,但要用一些前期成本来换取这些策略的易用性 类。
请注意,由于 Sample
不是新类型,因此不能保证它只会容纳一个 X
而不是两个,更多或可变数量(Maybe X
?Either X X
?)。因此,正如您将看到的,您的选项必须在结构内显式选择 X
,这可能是扩展自动派生为 not[=54= 的可能原因] 存在。
导出一个函数而不是多个
为了满足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)) :*: _))) = x
HasX
的最后一个实例匹配具有单个构造函数 (M1 C
) 的任何记录 (M1 D
),该构造函数具有多个 (:*:
) 字段 ( M1 S
), 第一个字段是类型 (Rec0
) X
.
(是的,通用实例很笨重。欢迎编辑。)
(要查看 Wrapper
的泛型类型的确切表示,请在 GHCi 控制台中检查 Rep Wrapper
。)
现在 Wrapper
的实例可以写成:
data Wrapper = Wrapper
{ x :: X
, i :: Int
}
deriving (Generic, HasX, Sample)
示例代码:
{-# 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
在这里,我有一些 class 是由 X
实现的,然后是另一个包含 X
.
Wrapper
我希望 Wrapper
通过其字段 x
派生 Sample
实例。
我知道我可以通过获取字段并自己为每个函数调用它来做到这一点,如图所示。
是否有一些标记或方法可以自动或仅执行一次?
这似乎类似于 DerivingVia
和 GeneralisedNewtypeDeriving
,但两者似乎都只针对 newtype
或可强制类型
这里有一些不需要任何扩展的策略,但要用一些前期成本来换取这些策略的易用性 类。
请注意,由于 Sample
不是新类型,因此不能保证它只会容纳一个 X
而不是两个,更多或可变数量(Maybe X
?Either X X
?)。因此,正如您将看到的,您的选项必须在结构内显式选择 X
,这可能是扩展自动派生为 not[=54= 的可能原因] 存在。
导出一个函数而不是多个
为了满足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)) :*: _))) = x
HasX
的最后一个实例匹配具有单个构造函数 (M1 C
) 的任何记录 (M1 D
),该构造函数具有多个 (:*:
) 字段 ( M1 S
), 第一个字段是类型 (Rec0
) X
.
(是的,通用实例很笨重。欢迎编辑。)
(要查看 Wrapper
的泛型类型的确切表示,请在 GHCi 控制台中检查 Rep Wrapper
。)
现在 Wrapper
的实例可以写成:
data Wrapper = Wrapper
{ x :: X
, i :: Int
}
deriving (Generic, HasX, Sample)