Vous êtes sur la page 1sur 27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

siguiente

anterior | ndice

Un puado de mnadas
La primera vez que hablamos de los funtores, vimos que son un concepto til para los valores que se pueden mapear. Luego, llevamos ese concepto un poco ms lejos y vimos los funtores aplicativos, que nos permite ver los valores de ciertos tipos de datos como una especia de valores con un contexto de forma que podemos aplicar funciones normales sobre ellos mantenido dicho contexto. En este captulos hablaremos de las mnadas, que simplemente son una versin ampliada de los funtores aplicativos, de la misma forma que los funtores aplicativos son una versin ampliada de los funtores. Cuando hablamos de los funtores vimos que es era posible mapear funciones sobre varios tipos de datos. Para lograrlo utilizbamos la clase de tipos F u n c t o r . Dada una funcin del tipo a > by un dato del tipo f a nos preguntbamos cmo mapeamos esta funcin sobre el dato de forma que obtuvisemos un resulto con el tipo f b . Vimos como mapear funciones sobre datos del tipo M a y b ea , del tipo [ a ] ,I Oa , etc. Incluso vimos como mapear funciones a > bsobre funciones r > ade forma que el resultado daba funciones del tipo r >b . Para contestar a la pregunta que nos hacamos de como mapear una funcin sobre un dato, lo nico que tenemos que hacer es mirar el tipo de f m a p :

f m a p: :( F u n c t o rf )= >( a>b )>fa>fb

Y luego hacer que funcione con el tipo de datos para el que estamos creando la instancia de F u n c t o r . Luego vimos que era posible mejorar los funtores. Decamos: Hey! qu pasa si tenemos una funcin a > bdentro del valor de un funtor? Como por ejemplo, J u s t( * 3 ) , y queremos aplicarla a J u s t5 . Qu pasara si en vez de aplicarla a J u s t 5la aplicamos a N o t h i n g ? O si tenemos [ ( * 2 ) , ( + 4 ) ]y queremos aplicarla a [ 1 , 2 , 3 ] ? Cmo hacemos para que funcione de forma general? Para ello utilizamos la clase de tipos A p p l i c a t i v e .

( < * > ): :( A p p l i c a t i v ef )= >f( a>b )>fa>fb

Tambin vimos que podamos tomar un valor normal e introducirlo dentro de un tipo de datos. Por ejemplo, podemos tomar un 1en introducirlo en un tipo M a y b ede forma que resulte en J u s t1 . O incluso podramos crear un [ 1 ] . O una accin de E/S que no realizara nada y devolviera un 1 . La funcin que realiza esta accin es p u r e .

aprendehaskell.es/content/Monadas.html

1/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Como ya dijimos, un valor aplicativo puede verse como un valor dentro de un contexto. Un valor adornado en trminos tcnicos. Por ejemplo, el carcter ' a 'es un simple carcter normal, mientras que J u s t' a 'tiene aadido un cierto contexto. En lugar de un C h a rtenemos un M a y b eC h a r , que nos dice que su valor puede ser un carcter o bien la ausencia de un carcter. Tambin es genial ver como la clase de tipos A p p l i c a t i v enos permite utilizar funciones normales con esos contextos de forma que los contextos se mantengan. Observa:

g h c i >( * )< $ >J u s t2< * >J u s t8 J u s t1 6 g h c i >( + + )< $ >J u s t" k l i n g o n "< * >N o t h i n g N o t h i n g g h c i >( )< $ >[ 3 , 4 ]< * >[ 1 , 2 , 3 ] [ 2 , 1 , 0 , 3 , 2 , 1 ]

Estupendo, ahora que los tratamos con valores aplicativos, los valores M a y b e arepresentan cmputos que pueden fallar, [ a ]representan cmputos que tienen varios resultados (cmputos no deterministas), I O arepresentan valores que tienen efectos secundarios, etc. Las mnadas son una extensin natural de los funtores aplicativos y tratan de resolver lo siguiente: si tenemos un valor en un cierto contexto, m a , cmo podemos aplicarle una funcin que toma un valor normal ay devuelve un valor en un contexto? Es decir, cmo podemos aplicarle una funcin del tipo a >mb ? Bsicamente lo que queremos es esta funcin:

( > > = ): :( M o n a dm )= >ma>( a>mb )>mb

Si tenemos un valor adornado y una funcin que toma un valor y devuelve un valor adornado, cmo pasamos el primer valor adornado a la funcin? . Esta ser la pregunta principal que nos haremos cuando trabajemos con las mnadas. Escribimos m aen lugar de f aya que mrepresenta mnadas, aunque las mnadas no son ms que funtores aplicativos que soportan la funcin > > = . Llamamos lazo a la funcin > > = . Cuando tenemos un valor normal ay una funcin normal a > bes muy fcil pasar ese valor a la funcin. Simplemente hay que aplicar la funcin a ese valor de forma normal. Pero cuando estamos trabajando con valores que vienen dentro de un cierto contexto, tenemos que tomarnos un tiempo para ver como estos valores adornados se pasan a las funciones y para ver como se comportan. No te preocupes, vers que es muy fcil.

Manos a la obra con Maybe


Ahora que ya tenemos una pequea idea del cometido de la mnadas, vamos a expandirla en detalle. Para sorpresa de nadie, M a y b ees una mnada, as que vamos a explorarlo un poco ms a ver si podemos combinar lo que ya sabemos con las mnadas.

Nota Llegados a este punto, asegrate de que entiendes los funtores aplicativos. Ser ms fcil si sabes como funcionan varias instancias de A p p l i c a t i v ey que tipo de cmputos representan, ya que las mnadas no son ms que una expansin de
aprendehaskell.es/content/Monadas.html 2/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

los funtores aplicativos.

Un valor de tipo M a y b e arepresenta un valor del tipo adentro del contexto de que ocurra un posible fallo. Un valor J u s t" d h a r m a " representa que la cadena " d h a r m a "est presente mientras que N o t h i n g representa su ausencia, o si vemos la cadena como el resultado de un cmputo, significar que dicho cmputo ha fallado. Cuando hablamos de M a y b ecomo funtor vimos que cuando mapeamos una funcin sobre l con f m a p , se mapea solo cuando es un valor J u s t , de otro modo N o t h i n gse mantiene como resultado ya que no hay nada sobre lo que mapear. Como esto:

g h c i >f m a p( + + " ! " )( J u s t" w i s d o m " ) J u s t" w i s d o m ! " g h c i >f m a p( + + " ! " )N o t h i n g N o t h i n g

Como funtor aplicativo funciona de forma similar. Sin embargo, los funtores aplicativos tambin poseen funciones dentro de los funtores. M a y b ees un funtor aplicativo de forma que cuando aplicamos < * >con una funcin contenida en un M a y b ea un valor contenido en un M a y b e , ambos deben ser valores J u s tpara que el resultado sea tambin un valor J u s t , en caso contrario el resultado ser N o t h i n g . Tiene sentido ya que si no tenemos o bien la funcin o bien el valor, no podemos crear un resultado a partir de la nada, as que hay que propagar el fallo:

g h c i >J u s t( + 3 )< * >J u s t3 J u s t6 g h c i >N o t h i n g< * >J u s t" g r e e d " N o t h i n g g h c i >J u s to r d< * >N o t h i n g N o t h i n g

Cuando utilizamos el estilo aplicativo con funciones normales para que acten con valores del tipo M a y b ees similar. Todos los valores deben ser J u s tsi queremos que el resultado tambin lo sea.

g h c i >m a x< $ >J u s t3< * >J u s t6 J u s t6 g h c i >m a x< $ >J u s t3< * >N o t h i n g N o t h i n g

Y ahora vamos a ver como podramos implementar > > =para M a y b e . Como ya hemos dicho, > > =toma un valor mondico y una funcin que toma un valor normal y devuelve otro valor mondico, de forma que aplica esta funcin al valor mondico. Cmo consigue hacerlo si la funcin solo toma valores normales? Bueno, para lograrlo hay que tomar en cuenta el contexto de ese valor mondico.

aprendehaskell.es/content/Monadas.html

3/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

En este caso, > > =tomar un valor con el tipo M a y b e ay una funcin de tipo a >` ` M a y b e by de alguna forma aplicar esta funcin para dar como resultado M a y b eb . Para imaginarnos como se hace, podemos apoyarnos en lo que ya sabemos de los funtores aplicativos. Digamos que tenemos una funcin del tipo \ x>J u s t( x + 1 ) . Toma un nmero, le aade 1y lo introduce en un J u s t :

g h c i >( \ x>J u s t( x + 1 ) )1 J u s t2 g h c i >( \ x>J u s t( x + 1 ) )1 0 0 J u s t1 0 1

Si le pasaramos como parmetro 1devolvera J u s t2 . Si le pasaramos 1 0 0devolvera J u s t1 0 1 . Simple. Ahora viene lo bueno: cmo pasamos un dato del tipo M a y b ea esta funcin? Si pensamos en M a y b ecomo un funtor aplicativo contestar a esta pregunta es bastante fcil. Si le pasamos un valor J u s t , toma el valor que contiene y le aplica la funcin. Si le pasamos N o t h i n g , mmm, bueno, tenemos la funcin pero no tenemos nada que pasarle. En este caso vamos a hacer lo mismo que hicimos anteriormente y diremos que el resultado ser N o t h i n g . En lugar de llamar a esta funcin > > = , vamos a llamarla a p p l y M a y b epor ahora. Toma un M a y b e ay una funcin que devuelve un M a y b e by se las ingenia para aplicar esa funcin a M a y b ea . Aqu est la funcin:

a p p l y M a y b e: :M a y b ea>( a>M a y b eb )>M a y b eb a p p l y M a y b eN o t h i n gf =N o t h i n g a p p l y M a y b e( J u s tx )f=fx

Vale, ahora vamos a jugar un poco con ella. La utilizamos de forma infija de forma que el valor M a y b eestar en la parte izquierda y la funcin a aplicar en la parte derecha:

g h c i >J u s t3` a p p l y M a y b e `\ x>J u s t( x + 1 ) J u s t4 g h c i >J u s t" s m i l e "` a p p l y M a y b e `\ x>J u s t( x+ +": ) " ) J u s t" s m i l e: ) " g h c i >N o t h i n g` a p p l y M a y b e `\ x>J u s t( x + 1 ) N o t h i n g g h c i >N o t h i n g` a p p l y M a y b e `\ x>J u s t( x+ +": ) " ) N o t h i n g

En este ejemplo vemos que cuando utilizamos a p p l y M a y b econ un valor J u s ty una funcin, la funcin simplemente se aplica al valor contenido en J u s t . Cuando la utilizamos con un valor N o t h i n g , el resultado final es N o t h i n g . Qu pasa si la funcin devuelve un N o t h i n g ? Vamos ver:

g h c i >J u s t3` a p p l y M a y b e `\ x>i fx>2t h e nJ u s txe l s eN o t h i n g J u s t3 g h c i >J u s t1` a p p l y M a y b e `\ x>i fx>2t h e nJ u s txe l s eN o t h i n g N o t h i n g

Justo como imaginbamos. Si el valor mondico de la izquierda es N o t h i n g , el resultado final es N o t h i n g . Y si la funcin de la derecha devuelve N o t h i n g , el resultado ser de nuevo N o t h i n g . Es muy parecido a cuando utilizabamos M a y b ecomo funtor aplicativo y obteniamos como resultado N o t h i n gsi en algn lugar haba un N o t h i n g .

aprendehaskell.es/content/Monadas.html

4/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Parace que para M a y b e , hemos averiguado como tomar un valor decorado y pasarlo a una funcin que toma un parmetro normal y devuelve otro valor decorado. Lo hemos conseguido teniendo en cuenta que los valores del tipo M a y b erepresentan cmputo que pueden fallar. Seguramente te este preguntado: y esto es til? Puede parecer que los funtores aplicativos son ms potentes que las mnadas, ya que los funtores aplicativos permiten tomar una funcin normal y hacer que opere sobre valores con un cierto contexto. Veremos que las mnadas pueden hacer exactamente lo mismo ya que son una versin mejorada de los funtores aplicativos, pero tambin veremos que pueden hacer ms cosas que los funtores aplicativos no pueden hacer. Volvermos con M a y b een un momento, pero primero, vamos a ver la clase de tipos que define las mnadas.

La clase de tipos de las mnadas


De la misma forma que los funtores tienen una clase F u n c t o ry que los funtores aplicativos tienen una clase A p p l i c a t i v e , las mnadas vienen con su propia clase de tipos: M o n a dWau! Quen lo hubiera imaginado? As es como luce su definicin:

c l a s sM o n a dmw h e r e r e t u r n: :a>ma ( > > = ): :ma>( a>mb )>mb ( > > ): :ma>mb>mb x> >y=x> > =\ _>y f a i l: :S t r i n g>ma f a i lm s g=e r r o rm s g

Empecemos por la primera lnea. Dice c l a s sM o n a dm w h e r e . Pero espera, no hemos dicho que las mnadas no son ms que funtores aplicativos ampliados? No debera haber una resitriccin de clase como c l a s s( A p p l i c a t i v em )=>M o n a d mw h e r ede forma que el tipo tenga que ser un funtor aplicativo primero antes de ser una mnada? Bueno, debera, pero cuando se creo Haskell, la gente que lo creo no pens que los funtores aplicativos encajaran bien en Haskell as que no aparece. Pero ten seguro que cada mnada es un funtor aplicativo, incluso aunque la declaracin de la clase M o n a ddiga lo contrario. La primera funcin que define la clase de tipos M o n a des r e t u r n . Es lo mismo que p u r epero con un nombre diferente. Su tipo es ( M o n a dm )= >a>ma . Toma un valor y lo introduce en el contexto por defecto que pueda albergar dicho valor. En otras palabras, toma un valor y lo introduce en una mnada. Siempre hace lo mismo que la funcin p u e d ade la clase de tipos A p p l i c a t i v e , por lo que ya estmos familiarizados al uso de r e t u r n . Ya hemos utilizado r e t u r ncuando trabajamos con la
aprendehaskell.es/content/Monadas.html 5/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

E/S. La utilizabamos para crear una accin de E/S que no hiciera nada salvo contener un valor. Con M a y b etoma un valor y lo introduce en un valor J u s t .

Nota Recordatorio: r e t u r nno se parece en nada al r e t u r nde la mayora de los otro lenguajes de programacin. No termina la ejecucin ni nada por el estilo, simplemente toma un valor normal y lo introduce en un contexto.

La siguiente funcin es > > =o lazo. Es como la aplicacin de funciones, solo que en lugar de tomar un valor y pasarlo a una funcin normal, toma un valor mondico (es decir, un valor en un cierto contexto) y lo pasa a una funcin que toma un valor normal pero devuelve otro valor mondico. A continuacin tenemos > > . No le vamos a prestar mucha atecin ahora mismo ya que viene con una implementacin por defecto y prcticamente nunca tendremos que implementarla cuando creemos instancias de M o n a d . La funcin final de la clase de tipos M o n a des f a i l . Nunca la utilizaremos explcitamente en nuestro cdigo. En cambio, Haskell la utilizar para permitir fallos en una construcin sintctica para las mnadas que veremos ms adelante. No tenemos que preocuparnos demasiado con f a i lahora mismo. Ahora que ya sabemos como luce la clase de tipos M o n a d , vamos a ver como es la instancia de M a y b epara la clase M o n a d :

i n s t a n c eM o n a dM a y b ew h e r e r e t u r nx=J u s tx N o t h i n g> > =f=N o t h i n g J u s tx> > =f =fx f a i l_=N o t h i n g

r e t u r nes lo mismo que p u r e , no hay que pensar mucho. Hacemos exactamente lo mismo que hacamos con A p p l i c a t i v e , introducimos un valor en J u s t . La funcin > > =es exactamente igual a p p l y M a y b e . Cuando le pasamos un valor del tipo M a y b e aa esta funcin, tenemos en cuenta el contexto y devolvemos N o t h i n gsi el valor a la izquierda es N o t h i n gya que no existe forma posible de aplicar la funcin con este valor. Si es un valor J u s ttomamos lo que hay dentro de l y aplicamos la funcin. Podemos probar un poco M a y b ecomo mnada:

g h c i >r e t u r n" W H A T ": :M a y b eS t r i n g J u s t" W H A T " g h c i >J u s t9> > =\ x>r e t u r n( x * 1 0 ) J u s t9 0 g h c i >N o t h i n g> > =\ x>r e t u r n( x * 1 0 ) N o t h i n g

Nada nuevo o emocionante en la primera lnea ya que ya hemos usado p u r econ M a y b ey sabemos que r e t u r nes igual
aprendehaskell.es/content/Monadas.html 6/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

que p u r esolo que con otro nombre. La siguientes dos lneas muestran como funciona > > =un poco ms. Fjate en como hemos pasado J u s t 9a la funcin \ x>r e t u r n( x * 1 0 ) , xtoma el valor 9dentro de la funcin. Parece como si fueramos capaces de extraer el valor de un M a y b esin utilizar un ajuste de patrones. Y an as no perdemos el contexto de los tipo M a y b e , porque cuando es N o t h i n g , el resultado de > > =ser N o t h i n gtambin.

En la cuerda floja
Ahora que ya sabemos como parar un valor del tipo M a y b e aa una funcin del tipo a >M a y b e bteniendo en cuenta el contexto de un posible fallo, vamos a ver como podemos usar > > =repetidamente para manejar varios valores M a y b ea . Pierre ha decidido tomar un descanso en su trabajo en la piscifactoria e intentar caminar por la cuerda floja. No lo hace nada mal, pero tiene un problema: los pjaros se posan sobre su barra de equilibrio! Aterrizan y se toman un pequeo respiro, hablan con sus respectivos amigos ovparos y luego se marchan en busca de algo de comida. Ha Pierre no le importara demasiado si el nmero de pjaros que se posan en cada lado de la barra fuera el mismo. Sin embargo, a menudo, todos los pjaros se posan en el mismo lado y desequilibran a Pierre tirndolo de la cuerda de forma embarazosa (utiliza un red de seguridad obviamente). Digamos que matiene el equilibrio si el nmero de pjaros posados a la izquierda y a la derecha de la barra no difere en ms de tres. As que si hay un pjaro en la parte derecha y otros cuatro pjaros en la parte izquierda no pasa nada. Pero si un quinto pjaro aterriza en la parte derecha pierde el quilibrio y cae. Vamos a simular un grupo de pjaros que aterrizan o inician el vuelo desde la barra y ver si Pierre sigue sobre la barra tras un nmero de eventos relacionados con estas aves. Por ejemplo, queremos saber que le pasar a Pierre si primero llega un pjaro al lado izquierdo de la barra, luego cuatro pjaros ms se posan sobre la parte derecha y luego el pjaro de la izquierda decide volar de nuevo. Podemos representar la barra con un par de enteros. El primer componente indicar el nmero de pjaros a la izquierda mientras que el segundo indicar el nmero de pjaros de la derecha:

t y p eB i r d s=I n t t y p eP o l e=( B i r d s , B i r d s )

Primero creamos un sinnimo para I n t , llamado pjaros (B i r d s ), ya que estamos utilizando enteros para representar el nmero de pjaros. Luego creamos otro sinnimo de tipos ( B i r d s ,B i r d s )y lo llamamos barra (P o l e ). A continuacin creamos una funcin que toma un nmero de pjaros y los posa sobre un determinado lado de la barra.
aprendehaskell.es/content/Monadas.html 7/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Aqu estn las funciones:

l a n d L e f t: :B i r d s>P o l e>P o l e l a n d L e f tn( l e f t , r i g h t )=( l e f t+n , r i g h t ) l a n d R i g h t: :B i r d s>P o l e>P o l e l a n d R i g h tn( l e f t , r i g h t )=( l e f t , r i g h t+n )

Bastante simple. Vamos a probarlas:

g h c i >l a n d L e f t2( 0 , 0 ) ( 2 , 0 ) g h c i >l a n d R i g h t1( 1 , 2 ) ( 1 , 3 ) g h c i >l a n d R i g h t( 1 )( 1 , 2 ) ( 1 , 1 )

Para hacer que los pjaros vuelen simplemente tenmos que pasarles a estas funciones un nmero negativo. Como estas funciones devuelven un valor del tipo P o l e , podemos encadenarlas:

g h c i >l a n d L e f t2( l a n d R i g h t1( l a n d L e f t1( 0 , 0 ) ) ) ( 3 , 1 )

Cuando aplicamos la funcin l a n d L e f t 1a ( 0 ,0 )obtenemos ( 1 ,0 ) . Luego aterrizamos un pjaro sobre el lado derecho, por lo que obtenemos ( 1 ,1 ) . Para terminar aterrizamos dos pjaros ms sobre el lado izquierdo, lo cual resulta en ( 3 ,1 ) . Aplicamos una funcin a algo escribirendo primero el nombre de la funcin y luego sus parmetros, pero en este caso sera mejor si la barra fuera primero y luego las funciones de aterrizar. Si creamos una funcin como:

x:f=fx

Podramos aplicar funciones escribiendo primero el parmetro y luego el nombre de la funcin:

g h c i >1 0 0:( * 3 ) 3 0 0 g h c i >T r u e:n o t F a l s e g h c i >( 0 ,0 ):l a n d L e f t2 ( 2 , 0 )

Utilizando esto podemos aterrrizar varios pjaros de un forma mucho ms legible:

g h c i >( 0 ,0 ):l a n d L e f t1:l a n d R i g h t1:l a n d L e f t2 ( 3 , 1 )

Genial! Es ejemplo es equivalente al ejemplo anterior en el que aterrizamos varias aves en la barra, solo que se ve ms limpio. As es ms obvio que empezamos con ( 0 ,0 )y luego aterrizamos un pjaro sobre la izquierda, otro sobre la derecha y finalmente dos ms sobre la izquierda.

aprendehaskell.es/content/Monadas.html

8/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Hasta aqu bien, pero, qu sucede si aterrizan diez pjaros sobre un lado?

g h c i >l a n d L e f t1 0( 0 , 3 ) ( 1 0 , 3 )

Diez pjaros en la parte izquierda y solo tres en la derecha? Seguro que Pierre ya debe estar volando por los aires en esos momentos. En este ejemplo es bastante obvio pero, y si tenemos una secuencia como esta?:

g h c i >( 0 , 0 ):l a n d L e f t1:l a n d R i g h t4:l a n d L e f t( 1 ):l a n d R i g h t( 2 ) ( 0 , 2 )

A primera vista puede parecer que todo esta bien pero si seguimos los pasos, veremos que en un determinado momento hay cuatro pjaros a la derecha y ninguno a la izquierda. Para arreglar esto debemos darle una vuelta de tuerca a las funciones l a n d L e f ty l a n d R i g h t . A partir de lo que hemos aprendido queremos que estas funciones sean capaces de fallar. Es decir, queremos que devuelvan una barra si Pierre consigue mantener el equilibrio pero que fallen en caso de que Pierre lo pierda. Y qu mejor manera de aadir el contexto de un posible fallo a un valor que utilizar M a y b e ! Vamos a reescribir estas funciones:

l a n d L e f t: :B i r d s>P o l e>M a y b eP o l e l a n d L e f tn( l e f t , r i g h t ) |a b s( ( l e f t+n )-r i g h t )<4=J u s t( l e f t+n ,r i g h t ) |o t h e r w i s e =N o t h i n g l a n d R i g h t: :B i r d s>P o l e>M a y b eP o l e l a n d R i g h tn( l e f t , r i g h t ) |a b s( l e f t-( r i g h t+n ) )<4=J u s t( l e f t ,r i g h t+n ) |o t h e r w i s e =N o t h i n g

En lugar de devolver un P o l eestas funciones devuelven un M a y b eP o l e . Siguen tomando el nmero de pjaros y el estado de la barra anterior, pero ahora comprueban si el nmero de pjaros y la posicin de estos es suficiente como para desquilibrar a Pierre. Utilizamos guardas para comprabar si diferencia entre el nmero de pjaros en cada lado es menor que cuatro. Si lo es devuelve una nueva barra dentro de un J u s t . Si no lo es, devuelven N o t h i n g . Vamos a jugar con estas pequeas:

g h c i >l a n d L e f t2( 0 , 0 ) J u s t( 2 , 0 ) g h c i >l a n d L e f t1 0( 0 , 3 ) N o t h i n g

Bien! Cuando aterrizamos pjaros sin que Pierre pierda el equilibrio obtenemos una nueva barra dentro de un J u s t . Pero cuando unos cunatos pjaros de ms acaban en un lado de la barra obtenemos N o t h i n g . Esto esta muy bien pero ahora hemos perido la posibilidad de aterrizar pjaros de forma repetiva sobre la barra. Ya no podemos usar l a n d L e f t1 ( l a n d R i g h t1( 0 , 0 ) )ya que cuando aplicamos l a n d R i g h t 1a ( 0 ,0 )no obtenemos un P o l e , sino un M a y b eP o l e . l a n d L e f t 1toma un P o l ey no un M a y b eP o l e . Necesitamos una forma de tomar un M a y b eP o l ey pasarlo a una funcin que toma un P o l ey devuelve un M a y b eP o l e .
aprendehaskell.es/content/Monadas.html 9/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Por suerte tenemos > > = , que hace exctamen lo que buscamos para M a y b e . Vamos a probarlo:

g h c i >l a n d R i g h t1( 0 , 0 )> > =l a n d L e f t2 J u s t( 2 , 1 )

Recuerda, l a n d L e f t 2tiene un tipo P o l e>M a y b eP o l e . No podemos pasarle directamente un valor del tipo M a y b e P o l eque es el resultado de l a n d R i g h t1( 0 ,0 ) , as que utilizamos > > =que toma un valor con un determinado contexto y se lo pasa a l a n d L e f t2 . De hecho > > =nos permite tratar valores M a y b ecomo valores en un contexto si pasamos N o t h i n ga l a n d L e f t2 , de forma que el resultado ser N o t h i n gy el fallo ser propagar:

g h c i >N o t h i n g> > =l a n d L e f t2 N o t h i n g

Gracias a esto ahora podemos encadenar varios aterrizajes que pueden consguir tirar a Pierre ya que > > =nos permite pasar valores mondicos a funciones que toman valores normales. Aqu tienes una secuencia de aterrizajes:

g h c i >r e t u r n( 0 , 0 )> > =l a n d R i g h t2> > =l a n d L e f t2> > =l a n d R i g h t2 J u s t( 2 , 4 )

Al principio hemos utilizado r e t u r npara insertar una barra dentro de un J u s t . Podramos haber aplicado l a n d R i g h t2 directamente a ( 0 ,0 ) ,h u b i r a m o sl l e g a d oa lm i s m or e s u l t a d o ,p e r od ee s t af o r m ap o d e m o su t i l i z a r ` ` > > =para cada funcin de forma ms consistente. Se pasa J u s t( 0 ,0 )a l a n d R i g h t2 , lo que devuelve J u s t( 0 ,2 ) . Luego se le pasa este valor a l a n d L e f t 2obteniendo J u s t( 2 ,2 )y as sucesivamente. Recuerda el ejemplo que dijimos que tirara a Pierre:

g h c i >( 0 , 0 ):l a n d L e f t1:l a n d R i g h t4:l a n d L e f t( 1 ):l a n d R i g h t( 2 ) ( 0 , 2 )

Como vemos no simula la interaccin con las aves correctamente ya que en medio la barra ya estara volando por los aires pero el resultado no lo refleja. Pero ahora vamos a probar a utilizar la aplicacin mondica (> > = ) en lugar de la aplicacin normal:

g h c i >r e t u r n( 0 , 0 )> > =l a n d L e f t1> > =l a n d R i g h t4> > =l a n d L e f t( 1 )> > =l a n d R i g h t( 2 ) N o t h i n g

Perfecto. El resultado final representa un fallo, que es justo lo que esperamos. Vamos a ver como se consigue este resultado. Primero, r e t u r n introduce ( 0 ,0 )en el contexto por defecto, convirtindolo en J u s t( 0 ,0 ) . Luego sucede J u s t( 0 , 0 )> > =l a n d L e f t1 . Como J u s t( 0 , 0 )es un valor J u s t ,l a n d L e f t 1es aplicado a ( 0 ,0 ) , obteniendo as J u s t( 1 ,0 )ya que Pierre sigue manteniendo el equilibrio. Luego nos encontramos con J u s t( 1 , 0 )
aprendehaskell.es/content/Monadas.html 10/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

> > =l a n d R i g h t 4lo cual resulta en J u s t( 1 ,4 )ya que Pierre sigue manteniendo el equilibrio, aunque malamente. Se aplica l a n d L e f t( 1 )a J u s t( 1 ,4 ) , o dicho de otra forma, se computa l a n d L e f t( 1 )( 1 , 4 ) . Ahora, debido a como funciona l a n d L e f t , esto devuelve N o t h i n gporque nuestro esta volando por los aires en este mismo momento. Ahora que tenemos N o t h i n gcomo resultado, ste se pasado a l a n d R i g h t( 2 ) ,p e r oc o m oe su nv a l o r` ` N o t h i n g , el resultado es automticamente N o t h i n gya que no existe ningn valor que se puede aplicar a l a n d R i g h t( 2 ) . No podamos haber conseguido esto utilizando solo M a y b ecomo funtor aplicativo. Si lo intentas te quedars atascado, porque los funtores aplicativos no permiten que los valores aplicativos interactuen con los otros lo sufiente. Pueden, como mucho, ser utilizados como parmetros de una funcin utilizando el estilo aplicativo. Los operadores aplicativos tomarn los resultados y se los pasarn a la funcin de forma apropiada para cada funto aplicativo y luego obtendrn un valor aplicativo, pero no existe ninguna interaccin entre ellos. Aqu, sin embargo, cada paso depende del resultado anterior. Por cada aterrizaje se examina el resultado anterior y se comprueba que la barra est balanceada. Esto determina si el aterrizaje se completar o fallar. Podemos divisar una funcin que ignora el nmero de pjaros en la barra de equilibrio y simplemente haga que Pierre caiga. La llamaremos b a n a n a :

b a n a n a: :P o l e>M a y b eP o l e b a n a n a_=N o t h i n g

Ahora podemos encadenar esta funcin con los aterrizajes de las aves. Siempre hara que Pierre se caiga ya que ignora cualquier cosa que se le pasa y devuelve un fallo. Compruebalo:

g h c i >r e t u r n( 0 , 0 )> > =l a n d L e f t1> > =b a n a n a> > =l a n d R i g h t1 N o t h i n g

El valor J u s t( 1 ,0 )se le pasa a b a n a n a , pero este produce N o t h i n g , lo cual hace que el resultado final sea N o t h i n g . Menuda suerte. En lugar de crear funciones que ignoren el resultado y simplemente devuelvan un valor mondico, podemos utilizar la funcin > >cuya implementacin por defecto es esta:

( > > ): :( M o n a dm )= >ma>mb>mb m> >n=m> > =\ _>n

Normalmente, si pasamos un valor a una funcin que toma un parmetro y siempre devuelve un mismo valor por defecto el resultado ser este valor por defecto. En cambio con la mnadas tambin debemos conseiderar el contexto y el siguinificado de stas. Aqu tienes un ejemplo de como funciona > >con M a y b e :

g h c i >N o t h i n g> >J u s t3 N o t h i n g g h c i >J u s t3> >J u s t4 J u s t4 g h c i >J u s t3> >N o t h i n g N o t h i n g


aprendehaskell.es/content/Monadas.html 11/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Si reemplazamos > >por > > =\ _>es fcil de ver lo que realmente sucede. Podemos cambiar la funcin b a n a n apor > >y luego un N o t h i n g :

g h c i >r e t u r n( 0 , 0 )> > =l a n d L e f t1> >N o t h i n g> > =l a n d R i g h t1 N o t h i n g

Ah lo tienes, garantizamos que Pierre se va ir al suelo! Tambin vale la pena echar un vistazo a como se veria esto si no hubiesemos tratado los valores M a y b ecomo valores en un contexto y no hubiersemos pasado las parmetros a las funciones como hemos hecho. As es como se vera una serie de aterrizajes:

r o u t i n e: :M a y b eP o l e r o u t i n e=c a s el a n d L e f t1( 0 , 0 )o f N o t h i n g>N o t h i n g J u s tp o l e 1>c a s el a n d R i g h t4p o l e 1o f N o t h i n g>N o t h i n g J u s tp o l e 2>c a s el a n d L e f t2p o l e 2o f N o t h i n g>N o t h i n g J u s tp o l e 3>l a n d L e f t1p o l e 3

Aterrizamos un pjaro y comprobamos la posibiliadad de que que ocurra un fallo o no. En caso de fallo devolvemos N o t h i n g . En caso contrario aterrizamos unos cuantos pjaros ms a la derecha y volemos a comprobar lo mismo una y otra vez. Convertir esto es un limpia concatenacin de aplicaciones mondicas con > > =es un ejemplo clsico de porque la mnada M a y b enos ahorra mucho tiempo cuando tenemos una secuecnia de cmputos que dependen del resultado de otros cmputos que pueden fallar. Fjate en como la implementacin de > > =para M a y b einterpreta exactamente la lgica de que en caso encontrarnos con un N o t h i n g , lo devolvemos como resultado y en caso contrario continuamos con lo que hay dentro de J u s t . En esta seccin hemos tomado varias funciones que ya teniamos y hemos visto que funcionan mejor si el valor que devuelven soporta fallos. Conviertiendo estos valores en valores del tipo M a y b ey cambiando la aplicacin de funciones normal por > > =obtenemos un mecanismo para manejar fallos casi de forma automtica, ya que se supone > > =preserva el contexto del valor que se aplica a una funcin. En este caso el contexto que tenan estos valores era la posibiliadad de fallo de forma que cuando aplicbamos funciones sobre estos valores, la posibilidad de fallo siempre era tomada en cuenta.

La notacin Do
Las mnadas son tan tiles en Haskell que tienen su propia sintaxis especial llamada notacin d o . Ya nos hemos topado con la notacin d ocuando reliazabamos acciones de E/S y dijimos que servia para unir varias de estas acciones en una sola.
aprendehaskell.es/content/Monadas.html 12/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Bueno, pues resulta que la notacin d ono solo funciona con I Osino que puede ser utilizada para cualquier mnada. El principio sigue siendo el mismo: unir varios valores mondicos en secuencia. Vamos a ver como funiona la notacin d oy porque es til. Considera el siguiente ejemplo familiar de una aplicacin mondica:

g h c i >J u s t3> > =( \ x>J u s t( s h o wx+ +" ! " ) ) J u s t" 3 ! "

Pasamos un valor mondico a una funcin que devuelve otro valor mondico. Nada nuevo. Fjate que en el ejemplo anterior, xse convierte en 3 , es decir, una vez dentro de la funcin lambda, J u s t 3pasa a ser un valor normal en vez de un valor mondico. Ahora, qu pasara si tuvisemos otro > > =dentro de la funcin?

g h c i >J u s t3> > =( \ x>J u s t" ! "> > =( \ y>J u s t( s h o wx+ +y ) ) ) J u s t" 3 ! "

Wau, un > > =anidado! En la funcin lambda interior, simplemente pasamos J u s t !a \ y>J u s t( s h o wx+ +y ) . Dentro de esta lambda, yse convierte en " ! " . xsigue siendo el 3que obtuvimos de la lambda exterior. Esto se parece a la siguiente expresin:

g h c i >l e tx=3 ;y=" ! "i ns h o wx+ +y " 3 ! "

La diferencia principal entre ambas es que los valores de la primera son valores mondicos. Son valores con el contexto de un posible fallo. Podemos remplazar cualquier valor por un fallo:

g h c i >N o t h i n g> > =( \ x>J u s t" ! "> > =( \ y>J u s t( s h o wx+ +y ) ) ) N o t h i n g g h c i >J u s t3> > =( \ x>N o t h i n g> > =( \ y>J u s t( s h o wx+ +y ) ) ) N o t h i n g g h c i >J u s t3> > =( \ x>J u s t" ! "> > =( \ y>N o t h i n g ) ) N o t h i n g

En la primera lnea, pasamos N o t h i n ga una funcin y naturalmente resulta en N o t h i n g . En la segunda lnea pasamos J u s t 3a la funcin de forma que xse convierte en 3 , pero luego pasamos N o t h i n ga la funcin lambda interior as que el resultado es tambin N o t h i n g . Todo esto es parecido a ligar nombres con ciertos valores utilizando las expresiones l e t , solo que en lugar de valores normales son valores mondicos. El siguiente ejemplo ilustra esta idea. Vamos a escribir lo mismo solo que cada valor M a y b eest en una sola lnea:

f o o: :M a y b eS t r i n g f o o=J u s t3 > > =( \ x> J u s t" ! "> > =( \ y> J u s t( s h o wx+ +y ) ) )

En lugar de escribir todos estas funciones lambdas, Haskell nos proporciona la sintaxis d oque nos permite escribir el
aprendehaskell.es/content/Monadas.html 13/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

anterior trozo de cdigo como:

f o o: :M a y b eS t r i n g f o o=d o x< -J u s t3 y< -J u s t" ! " J u s t( s h o wx+ +y )

Puede parecer que hemos ganado la habilidad de cosas de valores M a y b e sin tener que preocuparnos por comprobar en cada paso si dichos valores son valores J u s to valores N o t h i n gGenial! Si alguno de los valores que intentamos extraer es N o t h i n g , la expresin d oentera se reducir a N o t h i n g . Estamos extrayendo sus (probablemente existentes) valores y dejamos a > > = que se preocupe por el contexto de dichos valores. Es importante recordar que la notacin d oes solo una sintaxis diferente para encanedar valores mondicos. En una expresin d ocada lnea es un valor mondico. Para inspecionar el resultado de una lnea utilizamos < . Si tenemos un M a y b eS t r i n gy le damos una variable utilizando < , esa variable ser del tipo S t r i n g , del mismo modo que cuando utilizbamos > > =para pasar valores mondicos a las funciones lambda. El ltimo valor mondico de una expresin, en este caso J u s t( s h o wx + +y ) , no se puede utilizar junto a < -porque no tendra mucho sentido traducimos de nuevo la expresin d oa una ecandencin de aplicaciones > > = . Esta ltima lnea ser el resultado de unir toda la expresin d oen un nico valor mondico, teniendo en cuenta el hecho de que puede ocurrir un fallo en cualquiera de los pasos anteriores. Por ejemplo:

g h c i >J u s t9> > =( \ x>J u s t( x>8 ) ) J u s tT r u e

Como el parmetro a la izquierda de > > =es un valor J u s t , la funcin lambda es aplicada a 9y el resultado es J u s tT r u e . Si reescribimos esto en notacin d oobtenemos:

m a r y S u e: :M a y b eB o o l m a r y S u e=d o x< -J u s t9 J u s t( x>8 )

Si comparamos ambas es fcil deducir porque el resultado de toda la expresin d oes el ltimo valor mondico. La funcin r o u t i n eque escribimos anteriormente tambin puede ser escrita con una expresin d o .l a n d L e f ty l a n d R i g h ttoman el nmero de pjaros y la barra para producir una nueva barra dentro de un valor J u s t , a no ser que nuestro funambulista se caiga y produzca N o t h i n g . Utilizamos > > =porque cada uno de los pasos depende del anterior y cada uno de ellos tiene el contexto de un posible fallo. Aqu tienes dos pjaros posandose en lado izquierdo, luego otros dos
aprendehaskell.es/content/Monadas.html 14/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

pjaros posandose en lado derecho y luego otro ms aterrizando en la izquierda:

r o u t i n e: :M a y b eP o l e r o u t i n e=d o s t a r t< -r e t u r n( 0 , 0 ) f i r s t< -l a n d L e f t2s t a r t s e c o n d< -l a n d R i g h t2f i r s t l a n d L e f t1s e c o n d

Vamos a ver si funciona:

g h c i >r o u t i n e J u s t( 3 , 2 )

Lo hace! Genial! Cuando creamos esta funcin utilizando > > = , utilizbamos cosas como r e t u r n( 0 , 0 )> > =l a n d L e f t 2 , porque l a n d L e f t 2es una funcin que devuelve un valor del tipo M a y b e . Sin embargo con las expresiones d o , cada lnea debe representar un valor mondico. As que tenemos que pasar explcitamente cada P o l eanterior a las funciones l a n d L e f t yl a n d R i g h t . Si examinamos las variables a las que ligamos los valores M a y b e ,s t a r tsera ( 0 , 0 ) ,f i r s tsera ( 2 , 0 )y as sucesivamente. Debido a que las expresiones d ose escriben lnea a lnea, a mucha gente le puede parecer cdigo imperativo. Pero lo cierto es que son solo secuenciales, de forma que cada lnea depende del resultado de las lneas anteriores, junto con sus contextos (en este caso, dependen de si las anterioeres fallan o no). De nuevo, vamos a volver a ver como sera este cdigo si no tuvieramos en cuenta los aspectos mondicos de M a y b e :

r o u t i n e: :M a y b eP o l e r o u t i n e= c a s eJ u s t( 0 , 0 )o f N o t h i n g>N o t h i n g J u s ts t a r t>c a s el a n d L e f t2s t a r to f N o t h i n g>N o t h i n g J u s tf i r s t>c a s el a n d R i g h t2f i r s to f N o t h i n g>N o t h i n g J u s ts e c o n d>l a n d L e f t1s e c o n d

Fjate como en caso de no fallar, la tupla dentro de J u s t( 0 , 0 )se convierte en s t a r t , el resultado de l a n d L e f t2 s t a r tse convierte en f i r s t , etc. Si queremos lanzar a Pierre una piel de pltano en notacin d osolo tenemos que hacer lo siguiente:

r o u t i n e: :M a y b eP o l e r o u t i n e=d o s t a r t< -r e t u r n( 0 , 0 ) f i r s t< -l a n d L e f t2s t a r t N o t h i n g s e c o n d< -l a n d R i g h t2f i r s t l a n d L e f t1s e c o n d

Cuando escribirmos una lnea en la notacin d osin ligar el valor mondico con < , es como poner > >despus de ese
aprendehaskell.es/content/Monadas.html 15/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

valor mondico cuyo reulstado queremos que ignore. Secuenciamos el valor mondico pero ignoramos su resultado ya que no nos importa y es ms cmodo que escribir _ < -N o t h i n g , que por cierto, es lo mismo. Cuando utilizar la notacin d oy cuando utilizar > > =depende de ti. Creo que este ejemplo se expresa mejor escribiendo explcitamente los > > =ya que cada paso depende especficamente del anterior. Con la notacin d otenemos que especificar en que barra van a aterrizar los pjaros incluso aunque siempre aterrizen en la barra anterior. En la notacin d o , cuando ligamos valore mondicos a variables, podemos utilizar ajustes de patrones de la misma forma que los usbamos con las expresiones l e to con los parmetros de las funciones. Aqu tienes un ejemplo de uso de ajuste de patrones dentro de una expresin d o :

j u s t H: :M a y b eC h a r j u s t H=d o ( x : x s )< -J u s t" h e l l o " r e t u r nx

Hemos ajustado un patrn para obtener el primer carcter de la cadena " h e l l o "y luego lo devolvemos como resultado. As que J u s t Hse evalua a J u s t' h ' . Qu pasaria si este ajuste fallara? Cuando un ajuste de patrones falla en una funcin se utiliza el siguiente ajuste. Si el ajuste falla en todos los patrones de una funcin, se lanza un error y el programa podra terminar. Por otra parte si el ajuste falla en una expresin l e t , se lanza un error directamente ya que no existe ningn mecanismo que no lleve a otro patrn que ajustar. Cuando un ajuste falla dentro de una expresin d ose llama a la funcin f a i l . sta es parte de la clase de tipos M o n a dy nos permite ver este fallo como un fallo en el contexto del valor mondico en lugar de hacer que el programa termine. Su implementacin por defecto es:

f a i l: :( M o n a dm )= >S t r i n g>ma f a i lm s g=e r r o rm s g

As que por defecto hace que el programa termine, pero las mnadas que incorporan un contexto para un posible fallo (como M a y b e ) normalmente implementan el suyo propio. En M a y b ese implementa as:

f a i l_=N o t h i n g

Ignora el mensaje de error y devuelve N o t h i n g . As que cuando un ajuste falla dentro de un valor M a y b eque utiliza una expresin d o , el valor entero se reduce a N o t h i n g . Suele ser preferiable a que el programa termine. Aqu tienes una expresin d ocon un patrn que no se ajustar y por tanto fallar:

w o p w o p: :M a y b eC h a r w o p w o p=d o ( x : x s )< -J u s t" " r e t u r nx

El ajuste falla, as que sera igual a remplazar toda la lnea por N o t h i n g . Vamos a probarlo:

aprendehaskell.es/content/Monadas.html

16/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

g h c i >w o p w o p N o t h i n g

Este fallo en el ajuste de un patrn genera un fallo en el contexto de nuestra mnada en lugar de generar un fallo en el programa, lo cual es muy elegante.

La mnada lista
Hasta ahora hemos visto como los valores del tipo M a y b epueden verse como valores en un contexto de un posible fallo y que podemos incorportar el tratamiento de estos posibles fallos utilizando > > =para pasar los parmetros a las funciones. En esta seccin vamos a echar un vistazo a como podemos utilizar los aspectos mondicos de las listas llevanso as el no determinsmo a nuestro cdigo de forma legible. Ya hemos hablado de como las listas representan valores no deterministas cuando se utilizan como funtores aplicativos. Un valor como 5es determinista. Tiene un nico valor y sabemos exactamente cual es. Por otra parte, un valor como [ 3 , 8 , 9 ]consiste en varios resultados, as que lo podemos ver como un valor que en realidad es varios valores al mismo tiempo. Al utilizar las listas como funtores aplicativos vemos fcilmente este no determinismo:

g h c i >( * )< $ >[ 1 , 2 , 3 ]< * >[ 1 0 , 1 0 0 , 1 0 0 0 ] [ 1 0 , 1 0 0 , 1 0 0 0 , 2 0 , 2 0 0 , 2 0 0 0 , 3 0 , 3 0 0 , 3 0 0 0 ]

Todas la posibles soluciones de multiplicar los elementos de la izquierda por los elementos de la derecha aparecen en la lista resultado. Cuando trabajamos con el no determinismo, exsiten varias opciones que podemos tomar, as que bsicamente probamos todas ellas y por lo tanto el resultado tambin otro valor no determinista, solo que con unos cuantos valores ms. Este contexto de no determinismo se translada a las mnadas fcilmente. Vamos a ver como luce la instancia de M o n a d para las listas:

i n s t a n c eM o n a d[ ]w h e r e r e t u r nx=[ x ] x s> > =f=c o n c a t( m a pfx s ) f a i l_=[ ]

r e t u r nes lo mismo que p u r e , as que ya estamos familiarizados con ella. Toma un valor y lo introducie en el mnimo contexto por defecto que es capaz de albergar ese valor. En otras palabras, crea una lista que contiene como nico elemento dicho valor. Resulta til cuando necesitmos que un valor determinista interactue con otros valores no deterministas. Para entender mejor como funciona > > =con las listas veremos un ejemplo de su uso. > > =toma un valor con un contexto (un valor mondico) y se lo pasa a una funcin que toma valores normales y devuelve otro valor en el mismo contexto. Si esta funcin devolviera un valor normal en luegar de un valor mondico, > > =no sera muy til ya que depus de usarlo
aprendehaskell.es/content/Monadas.html 17/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

perderamos el contexto. De cualquier modo, vamos vamos a intentar pasar un valor no determinista a una funcin:

g h c i >[ 3 , 4 , 5 ]> > =\ x>[ x , x ] [ 3 , 3 , 4 , 4 , 5 , 5 ]

Cuando utilizamos > > =con M a y b e , el valor mondico se pasaba a la funcin teniendo en cuenta la existencia de un posible fallo. Aqu > > =se preocupa del no determinismo por nosotros. [ 3 , 4 , 5 ]es un valor no determinista y se lo hemos pasado a otra funcin que devuelve valores no deterministas tambin. El resultado final tambin es no determinista y contiene los posibles resultados de aplicar la funcin \ x>[ x , x ]a todos los elementos de [ 3 , 4 , 5 ] . Esta funcin toma un nmero y produce dos resultados: uno negado y otro igual que el original. De esta forma cuando utilizamos > > =para pasar la lista a esta funcin todos los nmeros son negados pero tambin se mantienen los originales. La xde la funcin lambda toma todos los posibles valores de la lista que pasamos como parmetro. Para ver como se consigue este resultado solo tenemos que ver la implementacin. Primero, empezamos con la list [ 3 , 4 , 5 ] . Luego mapeamos la funcin lambda sobre ella y obtenemos el siguiente resultado:

[ [ 3 , 3 ] , [ 4 , 4 ] , [ 5 , 5 ] ]

La funcin lambda se aplica a cada elemento por lo que obtenemos una lista de listas. Para terminar simplemente concatenamos las listas y punto final Acabamos de aplicar un funcin no determinista a una valor no determinista! El no determinismo tambin soporta la existencia de fallos. La lista vaca [ ]es muy parecido a N o t h i n gya que ambos representan la ausencia de un resultado. Por este motivo la funcin f a i lse define simplemente con la lista vaca. El mensaje de error se ignora.

g h c i >[ ]> > =\ x>[ " b a d " , " m a d " , " r a d " ] [ ] g h c i >[ 1 , 2 , 3 ]> > =\ _>[ ] [ ]

En la primera lnea se pasa una lista vaca a la funcin lambda. Como la lista no tienen ningn elemento, no podemos pasar nada a la funcin as que el resultado final es tambin la lista vaca. Es similiar a pasar N o t h i n ga una funcin. En la segunda lnea, cada elemento de la lista se pasa a la funcin, pero estos elementos son ignorados y la funcin simplemente devuelve una lista vaca. Como la funcin falla para todos los elementos de la lista, el resultado final es la lista vaca. Del mismo modo que pasaba con los valores del tipo M a y b e , podemos concatenar varios > > =propagando as el no deterministmo:

g h c i >[ 1 , 2 ]> > =\ n>[ ' a ' , ' b ' ]> > =\ c h>r e t u r n( n , c h ) [ ( 1 , ' a ' ) , ( 1 , ' b ' ) , ( 2 , ' a ' ) , ( 2 , ' b ' ) ]

Los elemenots de lista [ 1 , 2 ]se ligan a ny los elementos de [ ' a ' , ' b ' ]se ligan a c h . Luego, hacemos r e t u r n( n , c h ) (o [ ( n , c h ) ] ), lo que significa que tomamos una dupla ( n , c h )y la introducimos en el contexto mnimo por defecto. En este caso, se crea la lista ms pequea posible que pueda albergar ( n , c h )como resultado de forma que posea tan poco no
aprendehaskell.es/content/Monadas.html 18/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

determinismo como sea posible. Dicho de otro modo, el efecto del contexto es mnimo. Lo que estamos implementando es: para cada elemento en [ 1 , 2 ]y para cada elemento de [ ' a ' , ' b ' ]producimos una dupla para combinacin posible. En trminos generales, como r e t u r nlo nico que hace es introducir un valor en el contexto mnimo, no posee ningn efecto extra (como devolver un fallo en M a y b eo devolver en un valor an menos determinista en caso de las listas) sino que slamete toma un valor como resultado.

Nota Cuando tenemos varios valores no deterministas interactuando, podemos ver su cmputo como un rbol donde cada posible resultado representa una rama del rbol.

Aqu tienes la expresin anterior escrita con notacin d o :

l i s t O f T u p l e s: :[ ( I n t , C h a r ) ] l i s t O f T u p l e s=d o n< -[ 1 , 2 ] c h< -[ ' a ' , ' b ' ] r e t u r n( n , c h )

As parece ms obvio que ntoma cada posible valor de [ 1 , 2 ]y que c htoma cada posible valor de [ ' a ' , ' b ' ] . Del mismo modo que con M a y b e , estamos extrayendo valores normales de un valor mondico y dejamos que > > =se preocupe por el contexto. El contexto en este caso es el no determinismo. Cuando vemos las listas utilizando la notacin d opuede que nos recuerde a algo que ya hemos visto. Mira esto:

g h c i >[( n , c h )|n< -[ 1 , 2 ] ,c h< -[ ' a ' , ' b ' ]] [ ( 1 , ' a ' ) , ( 1 , ' b ' ) , ( 2 , ' a ' ) , ( 2 , ' b ' ) ]

S! Listas por comprensin! Cuando utilizbamos la notacin d o , ntomaba cada posible elemento de [ 1 , 2 ]y c htomaba cada posible elemento de [ ' a ' , ' b ' ]y luego introducamos ( n , c h )en el contexto por defecto (una lista unitaria) para devolverlo como resultado final sin tener que introducir ningn tipo de no determinismo adicional. En esta lista por comprensin hacemos exactamente lo mismo, solo que no tenemos que escribir r e t u r nal final para dar como resultado ( n , c h )ya que la lista por comprensin se encarga de hacerlo. De hecho, las listas por comprensin no son ms que una alternativa sintctica al uso de listas como mnadas. Al final, tanto las listas por comprensin como la notacin d ose traduce a una concatenacin de > > =que representan el no determinismo.

aprendehaskell.es/content/Monadas.html

19/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Las listas por comprensin nos perminten filtrar la lista. Por ejemplo, podemos filtrar una lista de nmero para quedarnos nicamente con los nmeros que contengan el dgito 7 :

g h c i >[x|x< -[ 1 . . 5 0 ] ,' 7 '` e l e m `s h o wx] [ 7 , 1 7 , 2 7 , 3 7 , 4 7 ]

Aplicamos s h o wa xpara convertir el nmero en una cadena y luego comprobamos si el carcter ' 7 'froma parte de en esa cadena. Muy ingenioso. Para comprender como se traduce estos filtros de las listas por comprensin a la mnada lista tenemos que ver la funcin g u a r dy la clase de tipos M o n a d P l u s . La clase de tipos M o n a d P l u srepresenta mnadas que son tambin monoides. Aqu tienes la definicin:

c l a s sM o n a dm= >M o n a d P l u smw h e r e m z e r o: :ma m p l u s: :ma>ma>ma

m z e r oes un sinnimo del m e m p t yque nos encontramos en la clase M o n o i dy m p l u scorreponde con m a p p e n d . Como las listas tambin son monoides a la vez que mnadas podemos crear una isntancia para esta clase de tipos:

i n s t a n c eM o n a d P l u s[ ]w h e r e m z e r o=[ ] m p l u s=( + + )

Para las listas m z e r orepresenta un cmputo no determinista que no devuelve ningn resultado, es decir un cmputo que falla. m p l u sune dos valores no deterministas en uno. La funcin g u a r dse define as:

g u a r d: :( M o n a d P l u sm )= >B o o l>m( ) g u a r dT r u e=r e t u r n( ) g u a r dF a l s e=m z e r o

Toma un valor booleano y si es T r u e , introduce ( )en el mnimo contexto por defecto. En caso contrario devuleve un valor mondico que representa un fallo. Aqu la tienes en accin:

g h c i >g u a r d( 5>2 ): :M a y b e( ) J u s t( ) g h c i >g u a r d( 1>2 ): :M a y b e( ) N o t h i n g g h c i >g u a r d( 5>2 ): :[ ( ) ] [ ( ) ] g h c i >g u a r d( 1>2 ): :[ ( ) ] [ ]

Parece interesante pero, es til? En la mnada lista utilizamos esta funcin para filtrar una series de cmputos no deterministas. Observa:

g h c i >[ 1 . . 5 0 ]> > =( \ x>g u a r d( ' 7 '` e l e m `s h o wx )> >r e t u r nx ) [ 7 , 1 7 , 2 7 , 3 7 , 4 7 ]

aprendehaskell.es/content/Monadas.html

20/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

El resultado es el mismo que la lista por comprensin anterior. Cmo consigue g u a r deste resultado? Primero vamos a ver se utiliza g u a r djunto a > > :

g h c i >g u a r d( 5>2 )> >r e t u r n" c o o l ": :[ S t r i n g ] [ " c o o l " ] g h c i >g u a r d( 1>2 )> >r e t u r n" c o o l ": :[ S t r i n g ] [ ]

Si el predicado de g u a r dse satisface, el resultado es una lista con una tupla vaca. Luego utilizamos > >para ignorar esta tupla vaca y devolver otra cosa como resultado. Sin embargo, si g u a r dfalla, no alcanzaremos el r e t u r nya que si pasamos una lista vaca a una funcn con > > =el resultado siempre ser una lista vaca. g u a r dsimplemente dice: si el predicado es F a l s eentonces devolvemos un fallo, en caso contrario devolvemos un valor que contiene un resultado ficticio ( ) . Esto permite que el encadenamiento continue. As sera el ejemplo anterior utilizando la notacin d o :

s e v e n s O n l y: :[ I n t ] s e v e n s O n l y=d o x< -[ 1 . . 5 0 ] g u a r d( ' 7 '` e l e m `s h o wx ) r e t u r nx

Si hubiramos olvidado devolver xcomo resultado final con r e t u r n , la lista resultante sera una lista de tuplas vacas en lugar de una lista de enteros. Aqu tienes de nuevo la lista por comprensin para que compares:

g h c i >[x|x< -[ 1 . . 5 0 ] ,' 7 '` e l e m `s h o wx] [ 7 , 1 7 , 2 7 , 3 7 , 4 7 ]

Filtrar una lista por comprensin es igual que usar g u a r d .

El salto del caballo


Vamos a ver un problema que tiende a resolverse utilizando no determinismo. Digamos que tenemos un tablero de ajedrez y como nica pieza un caballo. Queremos saber si el caballo peude alcanzar una determinada posicin en tres movimientos. Utilizaremos una dupla de nmeros para representar la posicin del caballo en el tablero. El primer nmero representar la columna en la que est el caballo y el segundo representar la fila.

aprendehaskell.es/content/Monadas.html

21/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Vamos a crear un sinnimo de tipo para representar la posicin actual del caballo:

t y p eK n i g h t P o s=( I n t , I n t )

Digamos que el caballo empieza en ( 6 , 2 )Puede alcanzar ( 6 , 1 )en solo tres movimientos? Vamos a ver. Si empezamos en ( 6 , 2 ) , cul sera el mejor movimiento a realizar? Ya se, Todos! Tenemos el no determinismo a nuestra disposicin, as que en lugar de decidirnos por un movimiento, hagmoslos todos. Aqu tienes una funcin que toma la posicin del caballo y devuelve todos las posibles posiciones en las que se encontrar depus de moverse.

m o v e K n i g h t: :K n i g h t P o s>[ K n i g h t P o s ] m o v e K n i g h t( c , r )=d o ( c ' , r ' )< -[ ( c + 2 , r 1 ) , ( c + 2 , r + 1 ) , ( c 2 , r 1 ) , ( c 2 , r + 1 ) , ( c + 1 , r 2 ) , ( c + 1 , r + 2 ) , ( c 1 , r 2 ) , ( c 1 , r + 2 ) ] g u a r d( c '` e l e m `[ 1 . . 8 ]& &r '` e l e m `[ 1 . . 8 ] ) r e t u r n( c ' , r ' )

El caballo puede tomar un paso en horizontal o vertical y otros dos pasos en horizontal o vertical pero siempre haciendo un movimiento horizontal y otro vertical. ( c ' , r ' )toma todos los valores de los elementos de la lista y luego g u a r dse encarga de comprobar que la nueva posicion permanece dentro del tablero. Si no lo est, produce una lista vaca y por lo tanto no se alcanza r e t u r n( c ' , r ' )para esa posicin. Tambin se puede escribir esta funcin sin hacer uso de la mnada lista, aunque lo acabamos de hacer solo por diversin. Aqu tienes la misma funcin utilizando f i l t e r :

m o v e K n i g h t: :K n i g h t P o s>[ K n i g h t P o s ]
aprendehaskell.es/content/Monadas.html 22/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

m o v e K n i g h t( c , r )=f i l t e ro n B o a r d [ ( c + 2 , r 1 ) , ( c + 2 , r + 1 ) , ( c 2 , r 1 ) , ( c 2 , r + 1 ) , ( c + 1 , r 2 ) , ( c + 1 , r + 2 ) , ( c 1 , r 2 ) , ( c 1 , r + 2 ) ] w h e r eo n B o a r d( c , r )=c` e l e m `[ 1 . . 8 ]& &r` e l e m `[ 1 . . 8 ]

Ambas son iguales, as que elige la que creas mejor. Vamos a probarla:

g h c i >m o v e K n i g h t( 6 , 2 ) [ ( 8 , 1 ) , ( 8 , 3 ) , ( 4 , 1 ) , ( 4 , 3 ) , ( 7 , 4 ) , ( 5 , 4 ) ] g h c i >m o v e K n i g h t( 8 , 1 ) [ ( 6 , 2 ) , ( 7 , 3 ) ]

Funciona perfectamente! Toma una posicin y devuelve todas las siguientes posiciones de golpe. As que ahora que tenemos la siguiente posicin de forma no determinista, solo tenemos que aplicar > > =para pasrsela a m o v e K n i g h t . Aqu tienes una funcin que toma una posicin y devuelve todas las posiciones que se pueden alcanzar en tres movimientos:

i n 3: :K n i g h t P o s>[ K n i g h t P o s ] i n 3s t a r t=d o f i r s t< -m o v e K n i g h ts t a r t s e c o n d< -m o v e K n i g h tf i r s t m o v e K n i g h ts e c o n d

Si le pasamos ( 6 , 2 ) , el resultado ser un poco grande porque si existe varias formas de llegar a la misma posicin en tres movimientos, tendremos varios elementos repetidos. A continuacin sin usar la notacin d o :

i n 3s t a r t=r e t u r ns t a r t> > =m o v e K n i g h t> > =m o v e K n i g h t> > =m o v e K n i g h t

Al utiliza > > =obtenemos todos los posibles movimientos desde el inicio y luego cuando utilizamos > > =por segunda vez, para cada posible primer movimiento calculamos cada posible siguiente movimiento. Lo mismo sucede para el tercer movimiento. Introducir un valor en el contexto por defecto utilizando r e t u r npara luego pasarlo como parmetro utilizando > > =es lo mismo que aplicar normalemente la funcin a dicho valor, aunque aqu lo hemos hecho de todas formas. Ahora vamos a crear una funcin que tome dos posiciones y nos diga si la ltima posicin puede ser alcanzada con exctamente tres pasos:

c a n R e a c h I n 3: :K n i g h t P o s>K n i g h t P o s>B o o l c a n R e a c h I n 3s t a r te n d=e n d` e l e m `i n 3s t a r t

Generamos todas las posibles soluciones que se pueden generar con tres pasos y luego comprobamos si la posicin destino se encuentra dentro de estas posibles soluciones. Vamos a ver si podemos alcanzar ( 6 , 1 )desde ( 6 , 2 )en tres movimientos:

g h c i >( 6 , 2 )` c a n R e a c h I n 3 `( 6 , 1 ) T r u e
aprendehaskell.es/content/Monadas.html 23/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

S! Y de ( 6 , 2 )a ( 7 , 3 ) ?

g h c i >( 6 , 2 )` c a n R e a c h I n 3 `( 7 , 3 ) F a l s e

No! Como ejercicio, puedes intentar modificar esta funcin para que cuando se pueda alcanzar esta posicin te diga que pasos debes seguir. Luego, veremos como modificar esta funcin de forma que tambin pasemos como parmetro el nmero de pasos.

Las leyes de las mnadas


De la misma forma que lo funtores aplicativos, a la vez que lo funtores normales, las mnadas vienen con una serie de leyes que todas las mnadas que se precien deben cumplir. Solo porque algo tenga una instancia de la clase M o n a dno significa que sea una mnada, solo significa que ese algo tiene una instancia para la clase M o n a d . Para que un tipo sea realmente una mnada debe satisfacer las leyes. Estas leyes nos permiten asumir muchas cosas acerca del comportamiento del tipo. Haskell permite que cualquier tipo tenga una instancia de cualquier clase de tipos siempre que los tipos concuerden. No puede comprobar si las leyes de las mnadas se cumplen o no, as que si estamos creando una instancia para la clase M o n a d , tenemos que estar lo suficientemente seguros de que la mnada satisface las leyes para ese tipo. Los estar seguros de que los tipos que vienen en la biblioteca estndar cumplen estas leyes, pero luego, cuando creemos nuestras prpias mnadas, tendremos que comprobar manualmente si se cumplen las leyes o no. No te asuste, no son complicadas.

Identidad por la izquierda


La primera ley establece que tomamos un valor, lo introducimos en el contexto por defecto utilizando r e t u r ny luego pasamos el resultado a una funcin utilizando > > = , el resultado debe ser igual que aplicar la funcin directamente a ese valor. Informalmente: r e t u r nx> > = fes exactamente lo mismo que f x . Si vemos los valores mondicos como valores con un cierto contexto y r e t u r ntoma un valor y lo introduce en el contexto mnimo por defecto que puede albergar ese valor, tiene sentido que, como ese contexto en realidad es mnimo, al pasar el valor mondico a una funcin no debe haber mucha diferencia con aplicar la funcin a un valor normal, y de hecho, es exactamente lo mismo. Para la mnada M a y b e ,r e t u r nse define como J u s t . La mnada M a y b etrata acerca de posibles fallos, as que si tenemos un valor y lo introducimos en dicho contexto, tiene sentido tratar este valor como cmputo correcto, ya que, bueno, sabemos cual es ese valor. Aqu tienes un par de usos de r e t u r n :

g h c i >r e t u r n3> > =( \ x>J u s t( x + 1 0 0 0 0 0 ) ) J u s t1 0 0 0 0 3


aprendehaskell.es/content/Monadas.html 24/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

g h c i >( \ x>J u s t( x + 1 0 0 0 0 0 ) )3 J u s t1 0 0 0 0 3

En cambio para la mnada lista, r e t u r nintruce un valor en una lista unitaria. La implementacin de > > =para las listas recorre todos los elementos de la lista y les aplica una funcin, pero como solo hay un elemento en la lista, es lo mismo que aplicar la funcin a ese valor:

g h c i >r e t u r n" W o M "> > =( \ x>[ x , x , x ] ) [ " W o M " , " W o M " , " W o M " ] g h c i >( \ x>[ x , x , x ] )" W o M " [ " W o M " , " W o M " , " W o M " ]

Dijimos que para la mnada I O ,r e t u r nsimplemente creaba una accin que no tenia ningn efecto secundario y solo albergaba el valor que pasbamos como parmetro. As que tambin cumple esta ley.

Identidad por la derecha


La segunda ley establece que si tenemos un valor mondico y utilizamos > > =para pasarselo a r e t u r n , el resultado debe ser el valor mondico original. Formalemente: m> > =r e t u r nes igual que m . Esta ley puede parecer un poco menos obvia que la primera, pero vamos a echar un vistazo para ver porque se debe cumplir. Pasamos valores mondicos a las funciones utilizando > > = . Estas funciones toman valores normales y devuelven valores mondicos. r e t u r nes una tambin es una de estas funciones. Como ya sabemos, r e t u r nintroduce un valor en el contexto mnimo que pueda albergar dicho valor. Esto quiere decir que, por ejemplo para M a y b e , no introduce ningn fallo; para las listas, no introduce ningn no determinismo adicional. Aqui tienes una prueba con algunas mnadas:

g h c i >J u s t" m o v eo nu p "> > =( \ x>r e t u r nx ) J u s t" m o v eo nu p " g h c i >[ 1 , 2 , 3 , 4 ]> > =( \ x>r e t u r nx ) [ 1 , 2 , 3 , 4 ] g h c i >p u t S t r L n" W a h ! "> > =( \ x>r e t u r nx ) W a h !

Si echamos un vistazo ms de cerca al ejemplo de las listas, la implementacin de > > =para las listas es:

x s> > =f=c o n c a t( m a pfx s )

As que cuando pasamos [ 1 , 2 , 3 , 4 ]a r e t u r n , primero r e t u r nse mapea sobre [ 1 , 2 , 3 , 4 ] , devolviendo [ [ 1 ] , [ 2 ] , [ 3 ] , [ 4 ] ]y luego se concatena esta lista obteniendo as la original. La identida por la izquierda y la identadad por la derecha son leyes que establecen el comportamiento de r e t u r n . Es una funcin importante para convertir valores normales en valores mondicos y no sera tan til si el valor mondico que produciera hicera mucha ms cosas.

Asociatividad
aprendehaskell.es/content/Monadas.html 25/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

La ltima ley de las mnadas dice que cuando tenemos una cadena de aplicaciones funciones mondicas con > > = , no importa el orden en el que estn anidadas. Escrito formalmente: ( m> > =f )> > = ges igual a > > =( x>fx> > =g ) . Mmm... Qu esta pasando aqu? Tenemos un valor mondico, my dos funciones mondica fy g . Hacemos ( m> > =f ) > > =g , es decir, pasamos ma f , lo cual devuelve un valor mondico. Luego pasamos ese valor mondico a g . En la expresin m> > =( \ x>fx> > =g )tomamos un valor mondico y se lo pasamos a una funcin que pasa el resultado de f xa g . Quiz no es fcil ver como ambas expresiones son iguales, as que vamos a ver un ejemplo para aclarr las dudas. Recuerdas cuando el funambulista Pierra caminaba sobre una cuerda con ayuda de una barra de equilibrio? Para simular el aterrizaje de los pjaros sobre esta barra de equilibrio utilizbamos una cadena de funciones que podan fallar:

g h c i >r e t u r n( 0 , 0 )> > =l a n d R i g h t2> > =l a n d L e f t2> > =l a n d R i g h t2 J u s t( 2 , 4 )

Empezbamos con J u s t( 0 , 0 )y luego pasbamos este valor a la siguiente funcin mondica, l a n d R i g h t2 . El resultado de sta era otro valor mondico que pasbamos a la siguiente funcin de la cadena y as sucesivamente. Si mostramos la asociatividad de forma explcita, la expresin quedara as:

g h c i >( ( r e t u r n( 0 , 0 )> > =l a n d R i g h t2 )> > =l a n d L e f t2 )> > =l a n d R i g h t2 J u s t( 2 , 4 )

Pero tambin podemos esxpresarlo as:

r e t u r n( 0 , 0 )> > =( \ x> l a n d R i g h t2x> > =( \ y> l a n d L e f t2y> > =( \ z> l a n d R i g h t2z ) ) )

r e t u r n( 0 , 0 )es lo mismo que J u s t( 0 , 0 )y cuando se lo pasamos a la funcin lambda, xse convierte en ( 0 , 0 ) . l a n d R i g h ttoma un nmero de pjaros y una barra (una dupla de nmeros) y eso es lo que le pasamos. Devuelve J u s t ( 0 , 2 )y cuando se lo pasamos a la siguiente funcin lambda, yes ( 0 , 2 ) . Continua hasta el ltimo aterrizaje de pjaros que produce J u s t( 2 , 4 ) , que de hecho es el resultado final de la expresin. Resumiendo, no importa como anides el paso de valores mondicos, lo que importa es su significado. Otra forma de ver esta ley sera: consideremos la composicin de dos funciones, fy g . La composicin de funciones se implementa como:

( . ): :( b>c )>( a>b )>( a>c ) f.g=( \ x>f( gx ) )

El tipo de ges a > by el de fes b >c , y las unimos en una nueva funcin con tipo a > ccuyo parmetro ser pasado entre las funciones anteriores. Y ahora, qu pasaria si estas dos funciones fueran mondicas? es decir qu pasaria si estas funciones devolvieran valores mondicos? Si tuvieramos una funcin del tipo a >mb , no podramos pasar su resultado directamente a una funcin del tipo b >mc , ya que esta funcin solo acepta valores normales y no mondicos.
aprendehaskell.es/content/Monadas.html 26/27

03/04/13

Un puado de mnadas Aprende Haskell por el bien de todos! v0 documentation

Sin embargo podemos utilizar > > =para poder permitirlo. As que si utilizamos > > = , podemos definir la composicin de dos funciones mondicas como:

( < = < ): :( M o n a dm )= >( b>mc )>( a>mb )>( a>mc ) f< = <g=( \ x>gx> > =f )

Ahora podemos componer nuevas funciones mondicas a partir de otras:

g h c i >l e tfx=[ x , x ] g h c i >l e tgx=[ x * 3 , x * 2 ] g h c i >l e th=f< = <g g h c i >h3 [ 9 , 9 , 6 , 6 ]

Genial Y qu tiene que ver esto con la ley de asociatividad? Bueno, cuando vemos la ley como una ley de composiciones, sta dice que f < = <( g< = <h )debe ser igual a ( f< = <g )< = <h . Es otra forma de decir que para las mnadas, no importa el orden del anidamiento. Si traducimos las dos primeras leyes para que utilicen < = < , entonces, la primera ley dice que para cada funcin mondica f , f< = <r e t u r nes lo mismo que fy la ley de identidad por la derecha dice que r e t u r n< = < fes tambin igual a f . Es parecido a lo que ocurre con las funciones normales, ( f.g ) . hes lo mismo que f . ( g.h ) , f.i des igual a fy i d . fes tambin igual a f . En este captulo hemos visto las bases de la mnadas y hemos aprendido a utilizar las mnadas M a y b ey las listas. En el siguiente captulo, echaremos un vistazo a un puado ms de mnadas y tambin aprenderemos como crear nuestras propias mnadas.

siguiente

anterior | ndice

aprendehaskell.es/content/Monadas.html

27/27

Vous aimerez peut-être aussi