我遇到了一种情况,我的代码可以使用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 :->
我们需要:->
包装器类型才能拥有类型为* -> *
的函数,但这并不能让我们很好地链接函数应用程序,就像我们可以使用<$>
和{{ 1}}用于普通的Applicatives。我可以使用帮助器来管理,例如
<*>
但是有一个通用的方法可以“解除”任何arity的功能。
如何使用上述实例的一些简单示例:
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 .)
所以,我的问题是:上面提到的类型类是什么,它们是否已经被一些库中的hackage提供了?通过Google搜索,我在linear-maps
中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"]
和multi-rec
中Functor2
提出了HFunctor
,但我的确无需这样做。
另外,是否有一些方法可以在没有HApplicative
包装器的情况下编写:->
或其他一些方法来使函数提升更容易?
答案 0 :(得分:2)
我倾向于认为的HFunctor是(* -> *) -> * -> *
- 也就是仿函数的合法函子。这与您正在考虑的特征有不同的特征。
以下是如何定义它,以及应用程序的“monoidal”版本。
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,但我不知道它是否打包过。我当然没有见过申请人。 B)我认为你坚持使用包装器,因为我们不能部分地应用类型同义词。