haskell - En la declaración de la clase Functor, ¿pueden las variables de tipo ser tipos de función?
applicative (3)
En Haskell, la clase
Functor
se declara como:
class Functor f where
fmap :: (a -> b) -> f a -> f b
¿Pueden las variables de tipo
a
y
b
ser tipos de función, o deben ser tipos sin función?
Si pueden ser tipos de función, ¿no es que la clase
Functor
vuelve efectivamente igual a la clase
Applicative
, en términos de hacer que
fmap
pueda aplicarse a funciones con un número arbitrario de argumentos?
Según lo que dice
Programación en Haskell
por Hutton:
Los functores abstraen la idea de mapear
fmap
una función sobre cada elemento de una estructura. Losfmap
generalizan esta idea para permitir que sefmap
funciones de mapeofmap
con cualquier número de argumentos, en lugar de restringirse a funciones con un solo argumento.
En aplicativo:
fmap0 :: a -> f a fmap0 = pure fmap1 :: (a -> b) -> f a -> f b fmap1 g x = pure g <*> x fmap2 :: (a -> b -> c) -> f a -> f b -> f c fmap2 g x y = pure g <*> x <*> y fmap3 :: (a -> b -> c -> d) -> f a -> f b -> f c -> f d fmap3 g x y z = pure g <*> x <*> y <*> z
La clase
Applicative
se declara como:
class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
Gracias.
¿Pueden las variables de tipo
a
yb
ser tipos de función, o deben ser tipos sin función?
b
pueden ser de
cualquier
tipo, por lo que los tipos de función también.
Por ejemplo podemos usar:
fmap (+) [1,4,2,5]
Aquí
(+)
tiene tipo:
fmap :: Functor f => (a -> b ) -> f a -> f b
(+) :: Num c => c -> (c -> c)
Entonces aquí
b ~ c -> c
, y
b
es, por lo tanto, una función.
Un ejemplo donde
a
es una función es:
fmap ($ 2) [ (1+), (2*), (3-) ]
aquí tenemos como tipos:
fmap :: Functor f => (a -> b) -> f a -> f b
($ 3) :: Num c => (c -> d) -> d
Entonces
a ~ (c -> d)
aquí, aquí aplicamos
3
a todas las funciones de la lista.
Tenga en cuenta que aquí no
fmap
definiciones adicionales de
fmap
(como en
fmap
1
,
fmap
2
, ...).
Es solo el hecho de que
a
puede ser sustituido por un tipo
c -> d
, etc.).
Si pueden ser tipos de función, ¿no es que la clase
Functor
convierte efectivamente en la misma que la claseApplicative
?
No, dado que, por ejemplo, no se dice que se puede implementar
pure
para un
Functor
dado.
Imagine que, por ejemplo, crea un tipo de datos:
type Owner = String
data Owned a = Owned Owner a
entonces
Owned
se puede implementar para ser una instancia de
Functor
:
instance Functor Owned where
fmap f (Owned o x) = Owned o (f x)
Pero implementar un
pure :: a -> Owned a
no es posible: ¿por qué sería el propietario del objeto?
¿Pueden las variables tipo
a
yb
ser tipos de función?
- si seguro.
¿No es que la clase
Functor
vuelve efectivamente igual a la claseApplicative
no absolutamente no.
Si inserta un tipo de función en el
a
o
b
de la firma
fmap
, obtendrá cosas como
fmap :: ((x -> y) -> b) -> f (x -> y) -> f b
o
fmap :: (a -> p -> q) -> f a -> f (p -> q)
pero de manera crucial,
fmap
siempre toma exactamente un valor
f _
envuelto y escupe exactamente uno de esos valores.
Mientras tanto,
Applicative
permite aceptar
cualquier número
de valores envueltos, siempre que le asigne la función de procesar los valores contenidos.
b
pueden ser tipos de funciones.
Pueden ser de cualquier tipo.
De hecho, un
Functor
válido
debe
permitir que sean de cualquier tipo.
Para responder a su pregunta
Applicative
, probémoslo.
fmap :: (a -> b -> c) -> f a -> f (b -> c)
¡Ok genial!
Ahora puedo tomar un
fa
y convertirlo a
f (b -> c)
.
¿Pero entonces, qué?
No puedo aplicar
f (b -> c)
a un argumento.
No es una función;
Es un valor de mi tipo de functor.
Si tan solo tuviéramos una función con esta firma ...
superFmap :: f (b -> c) -> f b -> f c
Pero eso seguro se parece mucho a
(<*>) :: f (b -> c) -> f b -> f c
que es miembro de
Applicative
.
Por lo tanto, necesitamos
Applicative
para poder aplicar este resultado secundario.
Lo que dicen las otras respuestas es correcto.
Tampoco podemos implementar
pure
, por razones similares.
Pero es importante tener en cuenta que ni siquiera podemos obtener
(<*>)
en general, porque si pudiéramos, eso implicaría que cada
Functor
es
Apply
, que ciertamente tampoco es el caso.