Deriving 详解
Deriving 详解
本文介绍 Haskell 中 deriving 的详细用法。
Deriving 的常规用法
对于如下代码: 1
data A = MkA Int deriving (Show, Eq)
A
派生为了
Show
和 Eq
的实例,这使得我们可以打印
A
的实例,也可以对两个 A
类型的实例进行相等的比较,即:
1 | λ> MkA 3 |
StandaloneDeriving
Haskell 允许将类型的声明和派生分开写,这需要开启
StandaloneDeriving
扩展。
使用 StandaloneDeriving
有以下好处: 1.
可以使类型的定义和派生的实现不在同一个文件中 2.
可以实现更加具体的派生规则
例如: 1
2data A a = MkA a
deriving instance Eq a => Eq (A (Maybe a))MkA (Just 3) == MkA (Just 4)
的比较,但是会拒绝
MkA 3 == MkA 4
。
对 newtype 进行派生
看如下代码:
1 | newtype A = MkA Int deriving (Show) |
在这里 A
实际上是对 Int
的一个包装,我们希望可以实现在 A
上的四则运算,当写下
newtype A = MkA Int deriving (Show, Num)
时,会发现编译器报错:
1 | <interactive>:1:29: error: |
因为编译器仍然在类型 A
上去实现对 Num
的推导,而 A
本身并不是 Num
类型的实例,所以无法执行。
此时可以启用 GeneralisedNewtypeDeriving
扩展,当普通的派生在 newtype 上无法实现时,会自动考虑使用 newtype
包装的类型。开启扩展后便可以实现在 A
上的四则运算了:
1 | λ> |
派生任意 type class
1 |
|
在开启 DeriveAnyClass
扩展后,我们可以对任意的 class
进行派生。
派生策略
考虑上文提到的
1 | λ> |
既然 Num
可以在 newtype 上派生,Show
是不是也可以?答案是肯定的,通过开启 DerivingStrategies
扩展,可以手动指定派生的规则:
1 |
|
在上述代码中 deriving newtype (Show)
表示
Show
的派生依据 newtype 的策略,即 MkA 3
会被直接显示为 3
,deriving stock (Eq)
表示用普通的策略派生 Eq
,而
deriving anyclass (F)
则表明使用 anyclass
的策略进行派生。
Deriving via
Deriving via 是一种更广泛的表明派生策略的方式,它允许依照已经是某一
typec class 的实例来进行派生,使用 Deriving via 需要开启
DerivingVia
扩展。
1 | newtype A = MkA Int |
这里的 via 表明 A
类型根据 Int
的
Show
的方式进行派生。
via 可以更复杂,详见官方文档。