haskell functor

haskell - Diversión con fmap repetido



haskell do (2)

No puedo explicar por qué, pero aquí está la prueba del ciclo:

Supongamos que k >= 2 y fmap^(4k) :: (a -> b) -> F1 F2 F3 a -> F1 F2 F3 b , donde Fx representa un Functor desconocido / arbitrario. fmap^n significa fmap aplicado a n-1 fmap s, no iteración n-1 fmap . El inicio de la inducción se puede verificar a mano o consultando ghci.

fmap^(4k+1) = fmap^(4k) fmap fmap :: (x -> y) -> F4 x -> F4 y

la unificación con a -> b produce a = x -> y , b = F4 x -> F4 y , entonces

fmap^(4k+1) :: F1 F2 F3 (x -> y) -> F1 F2 F3 (F4 x -> F4 y)

Ahora, para fmap^(4k+2) debemos unificar F1 F2 F3 (x -> y) con (a -> b) -> F5 a -> F5 b .
Por lo tanto, F1 = (->) (a -> b) y F2 F3 (x -> y) deben unificarse con F5 a -> F5 b .
De ahí que F2 = (->) (F5 a) y F3 (x -> y) = F5 b , es decir, F5 = F3 y b = x -> y . El resultado es

fmap^(4k+2) :: F1 F2 F3 (F4 x -> F4 y) = (a -> (x -> y)) -> F3 a -> F3 (F4 x -> F4 y)

Para fmap^(4k+3) , debemos unificar a -> (x -> y) con (m -> n) -> F6 m -> F6 n) , dando a = m -> n ,
x = F6 m e y = F6 n , entonces

fmap^(4k+3) :: F3 a -> F3 (F4 x -> F4 y) = F3 (m -> n) -> F3 (F4 F6 m -> F4 F6 n)

Finalmente, debemos unificar F3 (m -> n) con (a -> b) -> F7 a -> F7 b , entonces F3 = (->) (a -> b) , m = F7 a y n = F7 b , por lo tanto

fmap^(4k+4) :: F3 (F4 F6 m -> F4 F6 n) = (a -> b) -> (F4 F6 F7 a -> F4 F6 F7 b)

y el ciclo está completo. Por supuesto, el resultado proviene de consultar ghci, pero tal vez la derivación arroja algo de luz sobre cómo funciona.

Estaba jugando con funtores, y noté algo interesante:

Trivialmente, id se puede instanciar en el tipo (a -> b) -> a -> b .

Con el funtor de lista tenemos fmap :: (a -> b) -> [a] -> [b] , que es lo mismo que el map .

En el caso del funtor ((->) r) (de Control.Monad.Instances ), fmap es la composición de la función, por lo que podemos instanciar fmap fmap fmap :: (a -> b) -> [[a]] -> [[b]] , que es equivalente a (map . map) .

Curiosamente, fmap ocho veces nos da (map . map . map) !

Entonces tenemos

0: id = id 1: fmap = map 3: fmap fmap fmap = (map . map) 8: fmap fmap fmap fmap fmap fmap fmap fmap = (map . map . map)

¿Este patrón continúa? ¿Por qué por qué no? ¿Hay una fórmula para cuántos fmap s necesito para mapear una función sobre una lista anidada n veces?

Traté de escribir un script de prueba para buscar una solución al caso n = 4 , pero GHC comienza a comer demasiada memoria a alrededor de 40 fmap s.


fmap una respuesta un poco más simple: el map es una especialización de fmap y (.) También es una especialización de fmap . Entonces, por sustitución, ¡obtienes la identidad que descubriste!

Si estás interesado en ir más allá, Bartosz Milewski tiene un buen writeup que usa Yoneda Lemma para hacer explícita la razón por la que la composición de funciones es una mónada.