Estoy tratando de hacer esta pregunta desde mi tarea:
Dado arbitrario
foo :: [[a]] -> ([a], [a])
, escriba una ley quefoo
satisfaga la función , involucrandomap
en listas y pares.
Algún contexto: soy un estudiante de primer año que toma un curso de programación funcional. Si bien el curso es bastante introductorio, el profesor ha mencionado muchas cosas fuera del programa de estudios, entre los cuales se encuentran los teoremas gratuitos. Entonces, después de intentar leer el documento de Wadler, calculé que concat :: [[a]] -> [a]
la ley map f . concat = concat . map (map f)
parece relevante para mi problema, ya que debemos tener foo xss = (concat xss, concat' xss)
dónde concat
y concat'
cuáles son las funciones de tipo [[a]] -> [a]
. Entonces foo
satisfacebimap (map f, map g) . foo = \xss -> ((fst . foo . map (map f)) xss, (snd . foo . map (map g)) xss)
.
Esta "ley" ya parece demasiado larga para ser correcta, y tampoco estoy seguro de mi lógica. Entonces pensé en usar un generador de teoremas gratis en línea , pero no entiendo qué lift{(,)}
significa:
forall t1,t2 in TYPES, g :: t1 -> t2.
forall x :: [[t1]].
(f x, f (map (map g) x)) in lift{(,)}(map g,map g)
lift{(,)}(map g,map g)
= {((x1, x2), (y1, y2)) | (map g x1 = y1) && (map g x2 = y2)}
¿Cómo debo entender esta salida? ¿Y cómo debo derivar la ley para la función foo
correctamente?
fuente
(\(a,b) -> (map f a, map f b)) . foo = foo . map (map f)
Respuestas:
Si
R1
yR2
son relaciones (digamos,R_i
entreA_i
yB_i
, coni in {1,2}
), entonceslift{(,)}(R1,R2)
son los pares de relaciones "levantadas", entreA1 * A2
yB1 * B2
, con*
denotar el producto (escrito(,)
en Haskell).En la relación elevada, dos pares
(x1,x2) :: A1*A2
y(y1,y2) :: B1*B2
están relacionados si y solo six1 R1 y1
yx2 R2 y2
. En su caso,R1
yR2
son funcionesmap g, map g
, el levantamiento también se convierte en una función:y1 = map g x1 && y2 = map g x2
.Por lo tanto, el generado
medio:
o, en otras palabras:
que escribiría como, usando
Control.Arrow
:o incluso, en estilo sin puntos:
Esto no es una sorpresa, ya que
f
se puede escribir comoy
F
,G
son funtores (en Haskell tendríamos que utilizar unanewtype
para definir una instancia funtor, pero lo que omitiremos, ya que es irrelevante). En este caso común, el teorema libre tiene una forma muy agradable: para cadag
,Esta es una forma muy agradable, llamada naturalidad (
f
puede interpretarse como una transformación natural en una categoría adecuada). Tenga en cuenta que los dosf
s anteriores en realidad se instancian en tipos separados, por lo que los tipos están de acuerdo con el resto.En su caso específico, dado
F a = [[a]]
que es la composición del[]
functor consigo mismo, por lo tanto, como era de esperar, obtenemosfmap_of_F g = fmap_of_[] (fmap_of_[] g) = map (map g)
.En cambio,
G a = ([a],[a])
es la composición de los functores[]
yH a = (a,a)
(técnicamente, el functor diagonal compuesto con el functor del producto). Tenemosfmap_of_H h = (h *** h) = (\x -> (h x, h x))
, de la cualfmap_of_G g = fmap_of_H (fmap_of_[] g) = (map g *** map g)
.fuente
g
total. Del mismo modo, dado que tenemosseq
que exigirg
que seamos estrictos. No estoy 100% seguro de las restricciones exactas, pero creo que deberían serlo. Sin embargo, no recuerdo dónde leí sobre eso, probablemente en la página del generador de teoremas gratuito hay algo de información.map
vsfmap
. Las personas continúan usando,map
ya que hace obvio que estamos tratando con listas (y no con otro functor). Del mismo modo,(***)
solo funciona en pares (y no en otros bifunctores). Probablemente lo estoy usando principalmente por su inflexibilidad, ya que en matemáticas tendemos a escribirf \times g
para aplicar el bifunctor del producto. Quizás tambiénbimap
debería tener su variante infija, como<$>
es una variante parafmap
.(***)
es más específicobimap
que porque solo funciona en pares en lugar de bifunctores arbitrarios, también es cierto quebimap
es más específico(***)
que porque solo funciona en funciones en lugar de en flechas arbitrarias. Re infix, eso no sería lo mismo parabimap
yfmap
, ya quebimap
toma 3 parámetros yfmap
solo toma 2.Lo mismo que la respuesta de @ chi con menos ceremonia:
No importa si cambia los
a
s ab
s antes o después de la función, obtendrá lo mismo (siempre que use algofmap
similar para hacerlo).fuente