¿Qué significa componer dos Funtores?


El ejercicio 5 de Haskell Typeclassopedia Sección 3.2 pide una prueba o contraejemplo sobre la declaración

La composición de dos Funtores es también un Funtor.

Al principio pensé que esto estaba hablando de componer los métodos fmap definidos por dos instancias separadas de un Functor, pero eso realmente no tiene sentido, ya que los tipos no coincidirían por lo que puedo decir. Para dos tipos f y f', los tipos de fmap serían ser fmap :: (a -> b) -> f a -> f b y fmap :: (a -> b) -> f' a -> f' b, y eso realmente no parece componible. Entonces, ¿qué significa componer dos Functors?

Author: recursion.ninja, 2013-11-04

4 answers

A Functor da dos asignaciones: una en el nivel de tipos de asignación de tipos a tipos (esto es el x en instance Functor x where), y otra en el nivel de cálculo de asignación de funciones a funciones (esto es el x en fmap = x). Está pensando en componer el mapeo a nivel de término, pero debería estar pensando en componer el mapeo a nivel de tipo; por ejemplo, dado

newtype Compose f g x = Compose (f (g x))

Puedes escribir

instance (Functor f, Functor g) => Functor (Compose f g)

? Si no, ¿por qué no?

 25
Author: Daniel Wagner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-30 12:56:25

De lo que se trata es de la composición de constructores de tipos como [] y Maybe, no de la composición de funciones como fmap. Así, por ejemplo, hay dos formas de componer [] y Maybe:

newtype ListOfMabye a = ListOfMaybe [Maybe a]
newtype MaybeOfList a = MaybeOfList (Maybe [a])

La afirmación de que la composición de dos Functors es un Functor significa que hay una forma formulaica de escribir una instancia Functor para estos tipos:

instance Functor ListOfMaybe where
    fmap f (ListOfMaybe x) = ListOfMaybe (fmap (fmap f) x) 

instance Functor MaybeOfList where
    fmap f (MaybeOfList x) = MaybeOfList (fmap (fmap f) x)

De hecho, la plataforma Haskell viene con el módulo Data.Functor.Compose eso te da un Compose escribe que hace esto "gratis":

import Data.Functor.Compose

newtype Compose f g a = Compose { getCompose :: f (g a) }

instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose x) = Compose (fmap (fmap f) x)

Compose es particularmente útil con la extensión GeneralizedNewtypeDeriving:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype ListOfMaybe a = ListOfMaybe (Compose [] Maybe a)
   -- Now we can derive Functor and Applicative instances based on those of Compose
   deriving (Functor, Applicative)

Tenga en cuenta que la composición de dos Applicatives es también un Applicative. Por lo tanto, puesto que [] y Maybe son Applicatives, también lo son Compose [] Maybe y ListOfMaybe. Componer Applicatives es una técnica realmente ordenada que se está volviendo cada vez más común en estos días, como una alternativa a los transformadores de mónadas para los casos en los que no se necesita toda la potencia de las mónadas.

 16
Author: Luis Casillas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-22 20:04:16

Realmente ayuda pensar en la interpretación categórica aquí, un funtor F: C -> D toma objetos (valores) y morfismos (funciones) a objetos y morfismos de una categoría C a objetos y morfismos en una categoría D.

Para un segundo funtor G : D -> E la composición de funtores G . F : C -> E está tomando el codominio de F fmap transformación para ser el dominio de la G fmap transformación. En Haskell esto se logra con un poco de newtype desenvolviendo.

import Data.Functor

newtype Comp f g a = Comp { unComp :: f (g a) }

compose :: f (g a) -> Comp f g a
compose = Comp

decompose :: Comp f g a -> f (g a)
decompose = unComp

instance (Functor f, Functor g) => Functor (Comp f g) where
  fmap f = compose . fmap (fmap f) . decompose
 13
Author: Stephen Diehl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-04 18:58:34

La composición de dos funciones es cuando pones una función dentro de otra función, como

round (sqrt 23)

Esta es la composición de las dos funciones round y sqrt. Del mismo modo, la composición de dos funtores es cuando pones un funtor dentro de otro funtor, como

Just [3, 5, 6, 2]

List es un funtor, y quizás también lo es. Puede obtener alguna intuición de por qué su composición también es un funtor si intenta averiguar qué debería hacer fmap con el valor anterior. De por supuesto que debe mapear sobre el contenido del funtor interno!

 12
Author: kqr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-04 18:42:29