haskell - Funciones y aplicativos para tipos de tipo(*->*)->*
functor applicative (1)
El HFunctor que tiendo a pensar es (* -> *) -> * -> *
- es decir, un funtor legítimo en los funtores. Esto tiene características diferentes a las que estás pensando.
Aquí se explica cómo definirlo, así como la versión "monoidal" de un aplicativo.
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))
Intentaré actualizar más adelante con algunas ideas sobre qué es esto y cómo usarlo.
Esto no es exactamente lo que estás pidiendo, me doy cuenta, pero me inspiré para probarlo en tu publicación.
También me interesaría su caso de uso específico.
A sus dos preguntas específicas A) El HFunctor que describe ha sido descrito anteriormente en varias ocasiones, creo que en particular por Gibbons, pero no sé si está empaquetado. Ciertamente no he visto el aplicativo antes. B) Creo que estás atascado con el envoltorio porque no podemos aplicar parcialmente los sinónimos de tipo.
Me encontré con una situación en la que mi código se beneficiaría con el uso de abstracciones similares a Functor
y Applicative
, pero para tipos de tipo (* -> *) -> *
. La definición de un funtor de tipo superior se puede hacer con RankNTypes
como este
class HFunctor f where
hfmap :: (forall x. a x -> b x) -> f a -> f b
Pero la versión superior de Applicative
es un poco más complicada. Esto es lo mejor que se me ocurre:
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 :->
Necesitamos el :->
tipo de envoltura para tener funciones con el tipo * -> *
, pero esto no nos permite encadenar la aplicación de la función como podemos con <$>
y <*>
para los aplicativos normales. Puedo manejar con ayudante como
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 .)
Pero sería bueno tener una forma general de "levantar" las funciones de cualquier aridad.
Algunos ejemplos simples de cómo se pueden usar las instancias anteriores:
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"]
Entonces, mi pregunta es: ¿cómo se llaman las clases de tipos anteriores y ya están provistas por alguna biblioteca en hackage? Al Functor2
en Google encontré Functor2
en linear-maps
y HFunctor
en multi-rec
pero ninguno de los dos hace exactamente lo que necesito.
Además, ¿hay alguna forma de escribir HApplicative
sin la :->
envoltura o alguna otra forma de facilitar el levantamiento de funciones?