Я столкнулся с ситуацией, когда мой код выиграл бы от использования абстракций Functor
и Applicative
-like, но для типов типов (* -> *) -> *
. Определение более высокоподобного функтора можно сделать с помощью 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 :->
Нам нужен тип обертки :->
для того, чтобы иметь функции с видом * -> *
, но это не позволяет нам хорошо использовать функциональное приложение, как мы можем, с <$>
и <*>
для обычных приложений. Я могу справиться с помощником, например
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"]
Итак, мой вопрос: каковы названные выше классы имен и они уже предоставлены какой-либо библиотекой в хаке? По googling я придумал Functor2
в linear-maps
и HFunctor
в multi-rec
, но ни то, что мне нужно.
Кроме того, существует ли способ написать HApplicative
без обертки :->
или каким-либо другим способом упростить работу с функциями?