Functors和Applicatives适用于类型(* -> *) -> *的情况

21

我遇到了这样一种情况:我的代码可以从使用类似FunctorApplicative的抽象类型中受益,但是对于形式为(* -> *) -> *的类型。定义一个高阶类型函数可以使用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中的Functor2multi-rec中的HFunctor,但它们都不能完全满足我的需求。

此外,是否有某种方法可以在不使用:->包装器或其他更轻松的函数提升方式的情况下编写HApplicative

1个回答

2

我想到的HFunctor是(* -> *) -> * -> *,即functor上的合法functor。它与你想到的那个有不同的特点。

以下是对其定义的说明,以及其上的"monoidal" applicative版本。

type Nat f g = forall a. f a -> g a

class HFunctor (f :: (* -> *) -> * -> *) where
    hfmap :: (Nat g h) -> Nat (f g) (f h)

data Prod f g a = Prod (f a) (g a)

class HFunctor f => HApplicative f where
    hpure  :: Nat g (f g)
    htensor :: Nat (Prod (f g) (f h)) (f (Prod g h))

我会尝试稍后更新一些关于这是什么以及如何使用它的想法。

我知道这不完全是你所要求的,但是受到了你的帖子的启发我想去尝试一下。

我也对你具体的用例感兴趣。

对于你的两个具体问题:A)你描述的 HFunctor 已经在多个场合被描述过,我认为特别是由 Gibbons 描述,但我不知道是否打包。我之前从未见过 Applicative。B)我认为你被这个包装器困住了,因为我们不能部分应用类型同义词。


区别在于您正在定义内部函子,而OP正在定义从某些对象具有“*-> *”类型的类别(也许这应该是Hask上的内部函子类别)到Hask的函子。 - Philip JF
没错 - 正如我所指出的,“这与你想的那个有不同的特点”。 - sclv
只是一个小的技术评论:f :: (* -> *) -> * -> * 不是“functor on functors”,即使 f 是一个 HFunctor,因为 * -> * 只代表所有类型构造器,它们不一定是 functors 本身。例如,OP 的 a :-> b 通常不是一个 functor,即使 ab 是(你希望 a 是一个反变 functor)。在记起这一点之前,我差点写了一些非常错误的东西... - Reid Barton
只是一个快速的自我推销,我最近上传了一个可爱的小包叫做MHask,它的(Endo)Functor基本上与你的HFunctor相同,但限制是类别的对象是Prelude.Monad的实例。一旦我把一些东西打磨好,我会稍后发布正式公告。 - Dan Burton
@ReidBarton 没错,这是一个需要记在心里的好澄清,谢谢。 - sclv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接