问题:
最近我在这里提出了以下问题,询问如何创建一个通用的映射函数,并为任意多态ADT(代数数据类型)创建一个通用的Functor
实例,例如Lists、Trees等:
Functor instance for generic polymorphic ADTs in Haskell?
现在,我正在尝试重新制定上述内容,以便与recursion-schemes
兼容。也就是说,我想要在一方面定义类型,在另一方面定义基本的functor,并使用Base
家族类型将它们相关联。
因此,不再执行以下操作:
data ListF a b = NilF | ConsF a b
newtype Fix f = Fix { unFix :: f (Fix f) }
type List a = Fix (ListF a)
我想要做到这个:
data ListF a b = NilF | ConsF a b
data List a = Nil | Cons a (List a)
type instance Base (List a) = ListF a
这样,我就可以利用
递归方案(recursion-schemes)
库的强大功能,同时仍然能够为这些多态类型定义一个通用的fmap
。不仅如此,使用“正常”的类型而不是类型同义词对于体验来说更加愉悦。
尝试:
一开始,我考虑在一方面上实现Bifunctor
实例,然后以某种方式强制或使它等于相应的Base
族实例。目前,我只能想到使用Data.Type.Equality
中的a:~:b
。以下是我的工作进展:{-# LANGUAGE TypeOperators, Rank2Types #-}
import Data.Bifunctor
import Data.Functor.Foldable
import Data.Type.Equality
gmap :: (Bifunctor p, Foldable (f a), Unfoldable (f b)) =>
(forall x. p x :~: Base (f x)) -> (a -> b) -> f a -> f b
gmap refl f = cata alg
where
alg = embed .
castWith (apply refl Refl) .
bimap f id .
castWith (apply (sym refl) Refl)
我的问题在于尝试定义一个Functor
实例。我不知道如何在定义实例时指定这些特定的类型约束。
我考虑过创建一个Equals
类型类,并进行以下操作:
instance (Bifunctor p, Foldable (f a), Unfoldable (f b), Equals (p a) (Base (f a)))
=> Functor f where
但我不知道是否可行,也不确定我的方法是否正确(例如,我不确定我对gmap
的定义是否正确)。
供参考,这是原始SO问题中通用gmap
的定义:
gmap :: (Bifunctor f) => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = unwrapFixBifunctor . cata alg . wrapFixBifunctor
where
alg = Fix . bimap f id
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
更新:
注意到以下gmap
的定义更加通用,不需要任何奇怪的类型级别等式应用:
gmap :: (Foldable t, Unfoldable d, Bifunctor p, Base d ~ p b, Base t ~ p a)
=> (a -> b) -> t -> d
gmap f = cata ( embed . bimap f id )
然而,我仍然无法找到一种创建具有类似类型约束的
Functor
实例的方法。
:~:
的作用是什么?如果您必须一直使用Refl
调用函数,那么它可能不实用。gmap f = cata ( embed . bimap f id )
并让编译器推断类型,我得到(Foldable t, Unfoldable d, Bifunctor p, Base d ~ p b, Base t ~ p a) => (a -> b) -> t -> d
。这个最通用的类型有什么问题吗?如果您愿意,可以使用更具体的类型。编译器对您的函数非常满意,所以我不知道实际问题是什么。fmap = gmap
创建一个Functor
实例。 - gonzaw