【发布时间】:2015-03-16 23:43:19
【问题描述】:
问题:
最近我在这里问了以下问题,询问如何为任意多态 ADT(代数数据类型)(如列表、树等)创建通用映射函数和 Functor 的通用实例:
Functor instance for generic polymorphic ADTs in Haskell?
现在,我正在尝试重新制定上述内容以与recursion-schemes 兼容。即,我不想定义基仿函数,然后将类型定义为其固定点,而是一方面定义类型,另一方面定义基仿函数,并使用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 实例的方法
【问题讨论】:
-
1.
:~:有什么意义?如果您必须一直使用Refl调用它,您的函数可能不切实际。 2. 如果我写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。这种最通用的类型有什么问题吗?如果你愿意,你可以有更具体的类型。编译器对你的函数非常满意,所以我不知道实际的问题是什么。 -
@user2405038 我想使用
fmap = gmap创建Functor的实例
标签: haskell recursion functor algebraic-data-types category-theory