haskell functor applicative

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. Los fmap generalizan esta idea para permitir que se fmap funciones de mapeo fmap 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 y b 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 clase Applicative ?

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 y b ser tipos de función?

- si seguro.

¿No es que la clase Functor vuelve efectivamente igual a la clase Applicative

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.