我不喜欢用模式匹配来解构总和类型值。它对我来说很陌生,因为它的尖锐风格。 我宁愿为总和类型编写和使用switch
函数。
我的问题通常是关于任何总和类型,但我以Either
为例进行演示:
data Processor cl cr r = MakeProcessor { l :: cl -> r, r :: cr -> r }
switch :: Processor cl cr r -> Either cl cr -> r
switch p ei =
case ei of
Left x -> l p x
Right x -> r p x
通过此扩展Either
:
取而代之的是:
map f e =
case e of
Left x -> Left x
Right x -> Right (f x)
我可以这样写:
map f = switch (MakeProcessor { l : Left, r : f >>> Right })
,在大多数情况下,我感觉更好。
为总和类型编写这样的扩展内容[Processor
,switch
]是机械工作。所以我想知道:有没有办法让编译器 [GHC] 为我做这件事?或者任何其他方法都可以以毫无意义的样式解构总类型值?
注意:我以Either
为例。Either
是一个错误的选择,因为许多读者认为我的问题专门针对Either
,因此将我指向either
。我应该使用不在基本库中的 sum 类型,例如
data Result a b = Fail a | Success b
either
函数允许您指定要应用于Left
和Right
值的单独函数Either a b
值:
switch p ei = either (l p) (r p) ei
您可以立即从两侧掉落ei
以获得
switch p = either (l p) (r p)
您可以使用函数的Applicative
实例来删除p
:
switch = either <$> l <*> r
对于任意 sum 类型,您需要提供适当的同胚性,这是像either
这样的函数的花哨名称,它将 sum 类型包装的值减少到包装类型。
简短的回答是否定的(特别是没有模板哈斯克尔)。
但是,就像函数式编程中的许多构造一样,您可以将其颠倒过来。 使用 Church/Scott 编码(当数据类型不是递归时,它们是相同的)。
newtype ChurchEither a b = ChurchEither { switch :: forall c. (a -> c) -> (b -> c) -> c }
您可以在其中免费获得消除器switch
,因为它是类型的定义,但您必须自己编写构造函数
left x = ChurchEither (l r -> l x)
right x = ChurchEither (l r -> r x)
所以你并没有真正获得任何样板。