繁体   English   中英

Haskell中泛型多态ADT的Functor实例?

[英]Functor instance for generic polymorphic ADTs in Haskell?

当将类别理论应用于泛型编程时,Haskell做得非常好,例如像recursion-schemes这样的库。 然而,我不确定的一件事是如何为多态类型创建通用仿函数实例。

如果你有一个多态类型,比如List或Tree,你可以创建一个从(Hask×Hask)到Hask的仿函数代表它们。 例如:

data ListF a b = NilF | ConsF a b  -- L(A,B) = 1+A×B
data TreeF a b = EmptyF | NodeF a b b -- T(A,B) = 1+A×B×B

这些类型在A上是多态的,但是关于B是固定点,如下所示:

newtype Fix f = Fix { unFix :: f (Fix f) }
type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)

但正如大多数人知道,列表和树木也都在通常意义上,他们代表的“容器”仿函数a的,这样你可以映射函数f :: a -> b获得的容器b的。

我试图弄清楚是否有办法以通用的方式将这些类型(固定点)作为Functor一个实例,但我不确定如何。 到目前为止我遇到了以下两个问题:


1)首先,必须有一种方法来定义任何多态固定点上的通用gmap 知道像ListFTreeF这样的类型是Bifunctors,到目前为止我已经得到了这个:

{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bifunctor

newtype Fix f = Fix { unFix :: f (Fix f) }

cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . unFix

-- To explicitly use inF as the initial algebra
inF :: f (Fix f) -> Fix f
inF = Fix

gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

在Haskell中,这给了我以下错误: Could not deduce (Functor (fa)) arising from a use of cata from the context (Bifunctor f)

我正在使用bifunctors包,它有一个WrappedBifunctor类型,专门定义了以下可以解决上述问题的实例: Bifunctor p => Functor (WrappedBifunctor pa) 但是,我不确定如何在Fix “提升”这种类型以便能够使用它

2)即使可以定义上面的通用gmap ,我也不知道是否可以创建一个具有fmap = gmapFunctor的通用实例,并且可以立即为那里的ListTree类型工作(以及以类似方式定义的任何其他类型)。 这可能吗?

如果是这样,是否可以使其与recursion-schemes兼容?

你可以说,如果你愿意接受你正在与bifunctors打交道的那一刻

cata :: Bifunctor f => (f a r -> r) -> Fix (f a) -> r
cata f = f . bimap id (cata f) . unFix

然后

gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

(在gmap ,我刚刚重新安排了类约束,以使作用域类型变量起作用。)

您也可以使用原始版本的cata ,但是您需要在gmap上使用FunctorBifunctor约束:

gmap :: forall a b f. (Bifunctor f, Functor (f a)) => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

你不能让你的gmap成为普通Functor类的一个实例,因为它需要是类似的东西

instance ... => Functor (\ x -> Fix (f x))

而且我们没有类型级别的lambda。 如果你反转f的两个参数,你可以这样做,但是你失去了“其他” Functor实例,需要再次为Bifunctor定义cata

[您可能还有兴趣阅读http://www.andres-loeh.de/IndexedFunctors/以获得更通用的方法。]

TBH我不确定这个解决方案对你有多大帮助,因为它仍然需要为这些定点newtype函数提供额外的新类型包装,但是我们在这里:

如果你做一些包装/展开,你可以继续使用你的通用cata

给出以下两个辅助函数:

unwrapFixBifunctor :: (Bifunctor f) => Fix (WrappedBifunctor f a) -> Fix (f a)
unwrapFixBifunctor = Fix . unwrapBifunctor . fmap unwrapFixBifunctor . unFix

wrapFixBifunctor :: (Bifunctor f) => Fix (f a) -> Fix (WrappedBifunctor f a)
wrapFixBifunctor = Fix . fmap wrapFixBifunctor . WrapBifunctor . unFix

你可以在f上定义gmap而不需要任何额外的约束:

gmap :: (Bifunctor f) => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = unwrapFixBifunctor . cata alg . wrapFixBifunctor
  where
    alg = inF . bimap f id

你可以Fix . f Fix . fFunctor经由newtype

我们可以实现一个Functor例如\\a -> Fix (fa)通过实施本“型级拉姆达”作为newtype

newtype FixF f a = FixF{ unFixF :: Fix (f a) }

instance (Bifunctor f) => Functor (FixF f) where
    fmap f = FixF . gmap f . unFixF

bifunctors包还提供了一个特别合适的Fix版本:

newtype Fix p a = In {out :: p (Fix p a) a}

这很容易成为一个Functor实例:

instance Bifunctor p => Functor (Fix p) where
  fmap f = In . bimap (fmap f) f . out

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM