Conjuntos, Funtores y Eq confusión
Una discusión surgió recientemente en el trabajo sobre los Conjuntos, que en Scala soportan el método zip
y cómo esto puede conducir a errores, por ejemplo,
scala> val words = Set("one", "two", "three")
scala> words zip (words map (_.length))
res1: Set[(java.lang.String, Int)] = Set((one,3), (two,5))
Creo que está bastante claro que Set
s no debería soportar una operación zip
, ya que los elementos no están ordenados. Sin embargo, se sugirió que el problema es que Set
no es realmente un funtor, y no debería tener un método map
. Ciertamente, usted puede meterse en problemas mediante el mapeo de un conjunto. Cambiar a Haskell ahora,
data AlwaysEqual a = Wrap { unWrap :: a }
instance Eq (AlwaysEqual a) where
_ == _ = True
instance Ord (AlwaysEqual a) where
compare _ _ = EQ
Y ahora en ghci
ghci> import Data.Set as Set
ghci> let nums = Set.fromList [1, 2, 3]
ghci> Set.map unWrap $ Set.map Wrap $ nums
fromList [3]
ghci> Set.map (unWrap . Wrap) nums
fromList [1, 2, 3]
Así que Set
no satisface la ley del funtor
fmap f . fmap g = fmap (f . g)
Se puede argumentar que esto no es un fallo de la operación map
en Set
s, sino un fallo de la instancia Eq
que definimos, porque no respeta la ley de sustitución, es decir, que para dos instancias de Eq
en A y B y una asignación f : A -> B
entonces
if x == y (on A) then f x == f y (on B)
Que no se sostiene para AlwaysEqual
(por ejemplo, considere f = unWrap
).
Es la ley de sustitución a ¿ley sensata para el tipo Eq
que debemos tratar de respetar? Ciertamente, otras leyes de igualdad son respetadas por nuestro tipo AlwaysEqual
(la simetría, la transitividad y la reflexividad están trivialmente satisfechas) por lo que la sustitución es el único lugar en el que podemos meternos en problemas.
Para mí, la substición parece una propiedad muy deseable para la clase Eq
. Por otro lado, algunos comentarios sobre una discusión reciente de Reddit incluyen
" La sustitución parece más fuerte de lo necesario, y es básicamente cociente el tipo, poniendo requisitos en cada función usando el tipo."
-- godofpumpkins
"También realmente no quiero sustitución/congruencia ya que hay muchos usos legítimos para valores que queremos equiparar pero que de alguna manera son distinguibles."
-- sclv
"La sustitución solo es válida para la igualdad estructural, pero nada insiste en que
Eq
es estructural."-- edwardkmett
Estos tres son todos bastante conocidos en la comunidad Haskell, así que dudaría en ir en contra de ellos e insistir en la sustitución de mis Eq
tipos!
Otro argumento en contra de que Set
sea un Functor
- es ampliamente aceptado que ser un Functor
le permite transformar los "elementos" de una "colección" mientras preserva la forma. Por ejemplo, esta cita en el wiki de Haskell (tenga en cuenta que Traversable
es una generalización de Functor
)
"Donde
Foldable
le da la capacidad de ir a través de la estructura procesando los elementos pero desechando la forma,Traversable
le permite hacer eso mientras preserva la forma y, por ejemplo, poniendo nuevos valores.""
Traversable
se trata de preservar la estructura exactamente como está."
Y en el Mundo Real Haskell
"...[Un] funtor debe preservar la forma. La estructura de una colección no debe verse afectada por un funtor; solo los valores que contiene debería cambiar."
Claramente, cualquier instancia de funtor para Set
tiene la posibilidad de cambiar la forma, reduciendo el número de elementos en el conjunto.
Pero parece como si Set
s realmente deberían ser funtores (ignorando el requisito Ord
por el momento - lo veo como una restricción artificial impuesta por nuestro deseo de trabajar eficientemente con conjuntos, no un requisito absoluto para cualquier conjunto. Por ejemplo, los conjuntos de funciones son una cosa perfectamente sensata a considerar. En cualquier case, Oleg ha mostrado cómo escribir instancias eficientes de Funtor y Mónada para Set
que no requieren una restricción Ord
). Hay demasiados usos agradables para ellos (lo mismo es cierto para la instancia no existente Monad
).
¿Puede alguien aclarar este desastre? ¿Debería Set
ser un Functor
? Si es así, ¿qué hace uno sobre el potencial para romper las leyes Funtor? ¿Cuáles deberían ser las leyes para Eq
, y cómo interactúan con las leyes para Functor
y la instancia Set
en particular?
3 answers
Otro argumento en contra de que
Set
sea unFunctor
- es ampliamente aceptado que ser unFunctor
le permite transformar los "elementos" de una "colección" mientras preserva la forma. [...] Claramente, cualquier instancia de funtor para Set tiene la posibilidad de cambiar la forma, reduciendo el número de elementos en el set.
Me temo que este es un caso de tomar la analogía de la "forma" como una condición definitoria cuando no lo es. Matemáticamente hablando, hay tal cosa como el poder establecer functor. De Wikipedia :
Conjuntos de potencia: El funtor de conjunto de potencia P: Set → Set asigna cada conjunto a su conjunto de potencia y cada función f: X → Y al mapa que envía U X X a su imagen f (U) {Y.
La función P(f) (fmap f
en el funtor de conjunto de potencia) no conserva el tamaño de su conjunto de argumentos, sin embargo, este es un funtor.
Si quieres una analogía intuitiva mal considerada, podríamos decir esto: en un estructura como una lista, cada elemento " se preocupa "por su relación con los otros elementos, y se" ofendería " si un funtor falso rompiera esa relación. Pero un conjunto es el caso límite: una estructura cuyos elementos son indiferentes entre sí, por lo que hay muy poco que se puede hacer para "ofenderlos"; lo único es si un funtor falso mapeara un conjunto que contiene ese elemento a un resultado que no incluye su "voz"."
(Ok, me callaré ahora...)
EDIT: I trunca los siguientes bits cuando me citó en la parte superior de mi respuesta:
Por ejemplo, esta cita en el wiki de Haskell (tenga en cuenta que
Traversable
es una generalización deFunctor
)"Donde
Foldable
le da la capacidad de ir a través de la estructura procesando los elementos pero desechando la forma,Traversable
le permite hacer eso mientras preserva la forma y, por ejemplo, poniendo nuevos valores.""
Traversable
es sobre la preservación de la estructura exactamente como está."
Aquí hay que señalar que Traversable
es una especie de especializada Functor
, no es una "generalización". Uno de los hechos clave sobre cualquier Traversable
(o, en realidad, sobre Foldable
, que Traversable
se extiende) es que requiere que los elementos de cualquier estructura tengan un orden lineal: puede convertir cualquier Traversable
en una lista de sus elementos (con Foldable.toList
).
Otro hecho menos obvio sobre Traversable
es que existen las siguientes funciones (adaptado de Gibbons & Oliveira, "La Esencia del Patrón Iterador"):
-- | A "shape" is a Traversable structure with "no content,"
-- i.e., () at all locations.
type Shape t = t ()
-- | "Contents" without a shape are lists of elements.
type Contents a = [a]
shape :: Traversable t => t a -> Shape t
shape = fmap (const ())
contents :: Traversable t => t a -> Contents a
contents = Foldable.toList
-- | This function reconstructs any Traversable from its Shape and
-- Contents. Law:
--
-- > reassemble (shape xs) (contents xs) == Just xs
--
-- See Gibbons & Oliveira for implementation. Or do it as an exercise.
-- Hint: use the State monad...
--
reassemble :: Traversable t => Shape t -> Contents a -> Maybe (t a)
Una instancia Traversable
para conjuntos violaría la ley propuesta, porque todos los conjuntos no vacíos tendrían el mismo Shape
-el conjunto cuyo Contents
es [()]
. A partir de esto, debería ser fácil demostrar que cada vez que intente reassemble
un conjunto, solo recuperaría el conjunto vacío o un singleton.
Lección? Traversable
"conserva la forma" en un sentido muy específico y más fuerte que Functor
.
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-10-07 05:36:13
Set es "solo" un funtor (no un Functor
) de la subcategoría de Hask donde Eq
es "agradable" (es decir, la subcategoría donde congruencia, sustitución, sostiene). Si los tipos de restricción estaban alrededor de desde entonces tal vez set sería un Functor
de algún tipo.
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-10-05 04:01:55
Bien, Set puede ser tratado como un funtor covariante, y como un funtor contravariante; usualmente es un funtor covariante. Y para que se comporte con respecto a la igualdad uno tiene que asegurarse de que sea cual sea la implementación, lo hace.
Con respecto al Conjunto.zip - es una tontería. Así como Set.cabeza (lo tienes en Scala). No debería existir.
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-10-11 22:29:09