我想这有点自我推销,但我会发布它,因为它非常相关。
不久前我写了一个名为generic-church 的小型库。这导出具有两个函数的单一类型类。要使用它,只需执行类似的操作
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.Church
data Nat = Z | S Nat
instance ChurchRep Nat
请注意,Nat 不必像 Foldable 那样是函子。
然后我们可以启动 GHCi 并执行类似的操作
*> (toChurch Z) 'a' (const 'b')
'a'
*> (toChurch $ S Z) 'a' (const 'b')
'b'
与foldr 不同,您可以自动将教堂表示转换回具体数据类型
*> fromChurch (\a b -> a) :: Nat
Z
*> fromChurch (a b -> b Z) :: Nat
S Z
现在,与 catamorphisms/folds 相比,一个明显的缺点是 generic-church 目前以一种有点幼稚的方式处理递归数据类型(尽管我现在正在寻找修复这个问题)。也就是说,它将任何递归类型视为在其教堂表示中展开了一层,因此Nat 的教堂表示为
type Nat' = forall c. c -> (Nat -> c) -> c
编辑,你的generalFunc
这是编写generalFunc 的代码,它实际上概括了maybe。我需要一个函数toChurchP,它位于generic-church-0.2 中,在我输入之前大约5 分钟上传。如果您想运行此代码,请务必使用cabal update :)
{-# LANGUAGE DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, ScopedTypeVariables #-}
module So where
import Data.Church
import GHC.Generics
import Data.Proxy
-- Generalize of const. Allows us to use more than just Either
-- for gmaybe
class GConst a r where
gconst :: r -> a
instance GConst r' r => GConst (a -> r') r where
gconst r = const (gconst r)
instance GConst r r where
gconst = id
gfail :: forall a e s e' r.
(ChurchRep a, GConst e' e, Church a r ~ (e' -> s -> r)) =>
e -> s -> a -> r
gfail e s a = toChurchP (Proxy :: Proxy r) a (gconst e :: e') s
这取决于gconst 吞下所有给失败结果e 的参数。一个聪明的定义应该允许类似
> gfail id (const 'a') $ Left 'b'
'b'
但我目前的表述需要-XIncoherentInstances(哎呀!)。我希望能更新一个能同时概括either 和maybe 的版本。
虽然如此,这仍然有点漂亮。
*So> gfail True id (Just False)
False
*So> gfail True (id :: Bool -> Bool) Nothing
True
*So> gfail 'a' (const 'b') (Left ())
'a'
*So> gfail 'a' (const 'b') (Right ())
'b'
*So> :set -XDeriveGeneric
*So> data MyMaybe = Fail | Yay Int deriving (Show, Generic)
*So> instance ChurchRep MyMaybe
*So> gfail 'a' (const 'b') (Yay 1)
'b'