我遇到了这样一种情况:我的代码可以从使用类似Functor
和Applicative
的抽象类型中受益,但是对于形式为(* -> *) -> *
的类型。定义一个高阶类型函数可以使用RankNTypes
来实现,像这样:
class HFunctor f where
hfmap :: (forall x. a x -> b x) -> f a -> f b
但是更高级的Applicative
版本有点棘手。这是我能想到的最好方式:
class HFunctor f => HApplicative f where
hpure :: (forall x. a x) -> f a
(<**>) :: f (a :-> b) -> f a -> f b
newtype (:->) a b x = HFunc (a x -> b x)
infixr 5 :->
为了拥有形式为* -> *
的函数,我们需要:->
包装类型,但这不能像对于普通应用器(Applicative)中的<$>
和<*>
那样方便地链式应用函数。我可以通过辅助函数来实现。
liftHA2 :: HApplicative f => (forall x. a x -> b x -> c x) -> f a -> f b -> f c
liftHA2 f fa fb = hpure (fun2 f) <**> fa <**> fb where
fun2 = HFunc . (HFunc .)
但是有一个通用的方式来“提升”任何元数的函数会很不错。
以下是一些简单的示例,说明如何使用上述实例:
data Example f = Example (f Int) (f String)
instance HFunctor Example where
hfmap f (Example i s) = Example (f i) (f s)
instance HApplicative Example where
hpure a = Example a a
Example (HFunc fi) (HFunc fs) <**> Example i s = Example (fi i) (fs s)
e :: Example []
e = Example [1,2,3] ["foo", "bar"]
e' :: Example ((,) Int)
e' = hfmap (length &&& head) e -- Example (3,1) (2, "foo")
e'' :: Example []
e'' = liftHA2 (++) e e -- Example [1,2,3,1,2,3] ["foo", "bar", "foo", "bar"]
所以,我的问题是:上面提到的这些类型类叫什么名字,是否已经由Hackage中的某个库提供?通过谷歌搜索,我找到了linear-maps
中的Functor2
和multi-rec
中的HFunctor
,但它们都不能完全满足我的需求。
此外,是否有某种方法可以在不使用:->
包装器或其他更轻松的函数提升方式的情况下编写HApplicative
?
f :: (* -> *) -> * -> *
不是“functor on functors”,即使f
是一个HFunctor
,因为* -> *
只代表所有类型构造器,它们不一定是 functors 本身。例如,OP 的a :-> b
通常不是一个 functor,即使a
和b
是(你希望a
是一个反变 functor)。在记起这一点之前,我差点写了一些非常错误的东西... - Reid BartonFunctor
基本上与你的HFunctor
相同,但限制是类别的对象是Prelude.Monad
的实例。一旦我把一些东西打磨好,我会稍后发布正式公告。 - Dan Burton