使用GHC进行无点解构



我不喜欢用模式匹配来解构总和类型值。它对我来说很陌生,因为它的尖锐风格。 我宁愿为总和类型编写和使用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 })

,在大多数情况下,我感觉更好。

为总和类型编写这样的扩展内容[Processorswitch]是机械工作。所以我想知道:有没有办法让编译器 [GHC] 为我做这件事?或者任何其他方法都可以以毫无意义的样式解构总类型值?


注意:我以Either为例。Either是一个错误的选择,因为许多读者认为我的问题专门针对Either,因此将我指向either。我应该使用不在基本库中的 sum 类型,例如

data Result a b = Fail a | Success b

either函数允许您指定要应用于LeftRight值的单独函数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)

所以你并没有真正获得任何样板。

最新更新