Last active
December 26, 2015 02:39
-
-
Save josejuan/7080358 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*** AVISO, si lees ésto podrías llegar a desaprender *** | |
Cuidado con las mónadas, yo estoy bastante seguro de no entenderlas (bien). | |
Me explico, el concepto "por definición" de mónada es bastante simple, | |
bastante confuso al principio (como todo, supongo) pero luego es simple | |
(simple en el sentido de que las reglas que se aplican son sencillas, | |
luego cada mónada ya... puede ser infumable de entender). | |
(Ojo, lo que sigue hasta el siguiente aviso es una explicación CONCEPTUAL, | |
no tiene relación con una visión práctica) | |
Una definición "free style" de mónada para un programador OOP podría ser | |
que, cuando "estás dentro" de una mónada (algo así como "estar en el ámbito | |
de la mónada") es lo mismo que en OOP cuando estás dentro del ámbito de | |
la instancia de un objeto (pero la mónada es mucho más potente). | |
Las similitudes (insisto "free style") son: | |
1. una instancia de objeto acarrea objetos implícitamente (están ahí sin | |
hacer nada), en una mónada, los objetos son acarreados también implícitamente. | |
2. en una instancia de objeto, puedes llamar a los métodos dentro del | |
ámbito de la instancia (¡ojo! incluyendo miembros heredados) asumiendo | |
implícitamente los datos en [1]. En una mónada ocurre lo mismo, puedes | |
llamar a funciones que accederán implícitamente a los datos en [1]. | |
Realmente, el contexto así descrito muestra las pocas similitudes, porque | |
hay montones de diferencias. | |
(Ojo, ahora buscar cualquier similitud con OOP llevará a la locura :) | |
Por ejemplo, las mónadas funcionan currificando el contexto, ésto sólo | |
se entiende porque *TODO ES INMUTABLE* (el contexto, en un lenguaje no | |
inmutable por definición, sería simplemente pasar los parámetros por valor), | |
ésto es **ALGO COMO** (free style) usando pseudo-C#/javascript lambdas | |
// ¡¡¡ESTO ES LA FUNCIÓN MONÁDICA f1 !!! | |
(var contexto1, var argumentos1) => { | |
// ¡¡¡ESTO ES LA FUNCIÓN MONÁDICA f2 !!! | |
resultado1 <= ((var contexto2, var argumentos2) { | |
... | |
})(argumentos); | |
var resultado2 = resultado1 + 314159265358979; | |
return resultado2; | |
} | |
¡¡PERO OJO!! la aplicación del operador (<=) ¡¡¡no es asignación!!!, la | |
forma en que funciona (que no opera) esa "pseudoasignación" depende de | |
cada mónada (aquí es donde empezamos a no entender nada de nada ¿eh?, | |
je, je, ...). | |
¡¡OTRO OJO!! ese return que vemos ahí **NO ES UN RETURN**, sino que METE | |
EL RESULTADO DEL CÓMPUTO EN LA MÓNADA (esto tampoco se entiende, lo se, | |
es un lío, ...). En este caso es al revés, siempre funciona igual ¡pero | |
opera de diferente forma según la mónada!. | |
Por ejemplo, la mónada listas [1..10] cuando devuelve un resultado al | |
aplicar una función monádica (digamos f), lo que hace es aplicar la función | |
monádica a cada elemento 1, 2, ... 10 (item de la lista) del contexto | |
(la lista ¡porque es una mónada de listas!), como dicha función tiene | |
por fuerza que devolver una lista (¡porque es una mónada de listas y cualquier | |
función monádica que actúe en dicha mónada debe devolver listas!), tenemos | |
una lista de listas, por eso el "return" ¡¡¡de la mónada listas!!! (otra | |
mónada hará otra cosa **TOTALMENTE DIFERENTE**) lo que hace (repito, porque | |
le da la gana, podría hacer otra cosa y seguir siendo una mónada) es concatenar | |
los resultados. | |
Es decir, si tenemos una mónada sobre X dato, cualquier función que sea | |
digna de llamarse mónadica sobre X deberá devolver X. | |
Por ejemplo, si estamos en la mónada listas, cualquier función que devuelva | |
el tipo listas podrá aplicarse dentro de la mónada listas (¡ésto es mejor | |
que OOP! en OOP dentro de una instancia de un objeto sólo pueden aplicarse | |
sus miembros, pero sólo los suyos, mientras que en las mónadas ¡puedes | |
definir a posteriori tu propio método y actuar DENTRO de la mónada [parecido | |
a como pasa con las extensiones de C#]). | |
Así, cualquier función que termine con la firma | |
... -> [a] | |
es monádica en la mónada listas. | |
Por ejemplo, la función generadora de listas | |
[1..n] | |
toma como argumento un número y devuelve una lista. | |
Si estamos en la mónada listas, y hacemos ésto | |
do | |
x <- [1..10] | |
return x | |
¡No estamos haciendo nada!, veamos que está pasando | |
Aquí `x` aunque no lo parezca, ¡¡¡es el argumento de una función!!! (te | |
he matado lo se), de la función definida por **TODO LO QUE HAY DEBAJO | |
DE ESA PSEUDOASIGNACION**. | |
De hecho puede ponerse como lo hemos puesto antes | |
// todo ésto, es lo que haya debajo de un "bind" (<-) | |
(contexto_mónada, x) => { | |
return x; | |
} | |
sí, al operador (<-) se le llama "bind" (no pseudoasignación :). | |
El bind, como he dicho antes siempre funciona igual (hace que todo lo | |
que haya debajo sea una función) ¡pero la operación que percibimos difiere | |
de la mónada!. | |
Bien, sigamos con el ejemplo, hemos dicho que "return" lo que hace **en | |
la mónada de listas** (en otra mónada hará otra cosa) es un "concat map | |
f" ¿y cual es nuestra f aquí? ¡pues la identidad, porque teniendo un x | |
devolvemos un simple x!. | |
Así, en ese return lo que hacemos es un "map id", es decir, si nos dan | |
un 1, tenemos un 1, si nos dan un 2, tenemos un 2, ... y todo ello, lo | |
concatenamos, vamos, que tenemos la lista tal cual. | |
Veamos el código otra vez | |
do | |
x <- [1..10] | |
return x | |
¡¡cuidado que el que actúa de forma diferente según la mónada es el "return"!! | |
el bind siempre actúa igual (haciendo que lo que tiene debajo sea la función | |
"f" que luego el return hace "concat map f" en la mónada listas). | |
Bien, así que el resultado del cómputo anterior es | |
[1..10] | |
menuda sorpresa... :P | |
Veamos lo anterior a cámara lenta. | |
Tenemos | |
do | |
x <- [1..10] | |
return x | |
y sabemos que lo de debajo del bind se convierte en una función f, digamos | |
function f(x) { | |
retornar (return x) | |
} | |
**ATENCIÓN** que `x` es un elemento y "return" **LO METE EN LA MÓNADA** | |
por lo que hacer una llamada como | |
f(3) | |
lo que devuelve **SOLO** en el caso de la mónada listas, en otra mónada | |
devolverá otra cosa | |
[3] | |
así, con | |
do | |
x <- [1..10] | |
return x | |
la mónada listas hace un | |
concat (map f [1..10]) | |
es decir, tenemos | |
concat ([[1],[2],[3], ..., [10]]) | |
es decir | |
[1,2,3,...,10] | |
¿Parece fácil?, si te lo parece, no lo has entendido :P | |
¿que pasa si hacemos | |
do | |
x <- [1..6] | |
[1..x] // ojo que no ponemos return | |
veamos, lo de debajo del bind se convierte en una función digamos | |
function f(x) { | |
retornar [1..x]; // ¡¡ojo que no es "return! | |
} | |
entonces la mónada listas hace un | |
concat (map f [1..6]) | |
por lo que tenemos | |
concat ([[1], [1,2], [1,2,3], ...) | |
por lo que es | |
[1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6] | |
¿Y si hacemos | |
do | |
x <- [1..6] | |
return [1..x] // ojo que SI ponemos return | |
veamos, lo de debajo del bind ahora es | |
function f(x) { | |
retornar (return [1..x]); // ojo que SI es "return" | |
} | |
entonces la mónada listas hace un | |
concat (map f [1..6]) | |
¡pero cuidado! dijimos que "return" mete las cosas en la mónada, que esa | |
cosa sea *AHORA* una lista (ej. [1..3]) no impide que lo meta en la mónada | |
y si en el primer ejemplo (de la identidad) era | |
f(3) retorna [3] | |
ahora | |
f(3) retorna [[1..3]] | |
por lo que tenemos | |
concat ([[[1]],[[1,2]],...,[[1,2,3,4,5,6]]]) | |
es decir | |
[[1],[1,2],[1,2,3],[1,2,3,4],[1,2,3,4,5],[1,2,3,4,5,6]] | |
Triple salto mortal, ¿y si... | |
do | |
x <- [1..3] | |
y <- "ABC" | |
return (x, y) | |
pues resulta que ahora tenemos dos bind, por lo que todo lo de debajo | |
del primer bind es una función y todo lo de debajo del segundo bind es | |
otra función (dentro de la primera, claro) | |
function f(x) { | |
function g(y) { | |
retornar (return (x, y)) | |
} | |
retornar lo que venga | |
} | |
convirtiendo a "código plano", tenemos primero | |
concat (map f [1..3]) | |
y luego, al expandir f | |
concat (map (\x -> concat (map g "ABC")) [1..3]) | |
es decir | |
concat (map (\x -> concat (map (\y -> return (x, y)) "ABC")) [1..3]) | |
que finalmente (sin mónadas, completamente código plano), es | |
concat (map (\x -> concat (map (\y -> [(x, y)]) "ABC")) [1..3]) | |
de ahí, que al hacer | |
do | |
x <- [1..3] | |
y <- "ABC" | |
return (x, y) | |
**CON** la mónada listas sea | |
[(1,'A'),(1,'B'),(1,'C'),(2,'A'),...,(3,'C')] | |
El cómo actúan (funcionan y operan) realmente el "return" y "bind" monádicos | |
depende de cada mónada y puede ser realmente diferente una de otras. | |
El tema entonces está en **COMPRENDER LAS PROPIEDADES GENERALES DE LAS | |
MÓNADAS** (aquellas que actúan con independencia de la expresión concreta | |
de una mónada concreta). | |
Por ejemplo, creo que el concepto de "aditividad" es comprendido (aunque | |
quizás no explícitamente sí de forma intuitiva) por cualquier persona | |
(incluso de muy corta edad). | |
Todos sabemos que podemos "añadir sal a la sopa", que podemos "añadir | |
una prenda a un vestido", que *NO* podemos "añadir aceite al agua", etc... | |
Las propiedades de "añadir" las comprendemos sin tener que ir a ejemplos | |
concretos (yo actualmente para comprender una mónada tengo que tener un | |
ejemplo concreto). | |
Para comprender las propiedades y "esencia" de las mónadas hay que ir | |
a la teoría de categorías, las propiedades del "return" y del "bind" *CREO* | |
que no se van mucho de lo dicho, pero yo no comprendo aún (y no se si | |
lo sabré algún día) cuales son esas propiedades interesantes que tienen | |
las mónadas y que generalizan los conceptos concretados anteriormente | |
(en la mónada listas, pero fácilmente aplicables a Maybe, Reader, Writer, | |
...). | |
En fin, espero que no hayas desaprendido mucho. | |
:) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment