Es bastante fácil de definir un operador comoaplicación de función anidada
(@) :: [x -> y] -> [x] -> [y]
que tiene una lista de funciones y una lista de entradas y devuelve una lista de salidas. Hay dos maneras obvias para implementar esta:
- Aplicar la primera función a la primera entrada, la segunda función a la segunda entrada, etc.
- Aplicar todas las funciones a cada entrada.
Cualquiera de las dos cosas es igualmente trivial de definir. Lo bueno de esto es que ahora se puede hacer algo como
foo :: X -> Y -> Z -> R
bar :: [X] -> [Y] -> [Z] -> [R]
bar xs ys zs = [foo] @@ xs @@ ys @@ zs
Esto generaliza a un número arbitrario de argumentos de la función.
Hasta ahora todo bien. Ahora para el problema: ¿Cómo puedo cambiar la firma tipo para @@
que la rúbrica tipo para bar
convierte
bar :: [X] -> [Y] -> [Z] -> [[[R]]]
No es difícil de implementar una función de este tipo; cualquiera de estos lo hará:
bar xs ys zs = map (\ x -> map (\ y -> map (\ z -> foo x y z) zs) ys) zs
bar xs ys zs = map (\ z -> map (\ y -> map (\ x -> foo x y z) xs) ys) zs
No soy quisquilloso sobre qué resultado obtengo. Pero no puedo entender cómo ajustar el operador @@
para hacer esto.
Una cosa obvia a probar es
(@@) :: [x -> y] -> [x] -> [[y]]
No es difícil de implementar esto, pero no le ayudará. Ahora usted tiene
[foo] @@ xs :: [[Y -> Z -> R]]
que no es una entrada válida para @@
. No hay una manera obvia de saber cuántos niveles de listas hay que alcanzar para llegar a la función; claramente este enfoque es un callejón sin salida.
He intentado varias otras posibles firmas de tipo, pero ninguna de ellas me acerca a la respuesta. ¿Alguien puede darme una solución o explicar por qué no existe?
Usa una clase de tipo. – dave4420
Irrelevante para la pregunta, sin embargo, su '(@@)' original (segunda versión) es equivalente a la instancia 'Applicative' para las listas. La primera versión, que aplica cada función a cada entrada, es la instancia 'Applicative' para' newLipe' ZipList'. –
¿Cuál es la diferencia entre 'cada' y 'cada'? – Prateek