Difference between revisions of "Meet Bob The Monadic Lover"

From HaskellWiki
Jump to navigation Jump to search
m (some monad code change: Bob has a conscience after all!)
m (better (I hope) function names for the monad part)
Line 256: Line 256:
 
"times" is just a "map" for Bob's loverDiary.
 
"times" is just a "map" for Bob's loverDiary.
   
In Haskell there is a class for this kind of types. I mean, does types
+
In Haskell there is a class for this kind of types. I mean, those types
 
that can be mapped as Bob's loverDiary can.
 
that can be mapped as Bob's loverDiary can.
   
Line 280: Line 280:
 
'''to be continued'''
 
'''to be continued'''
   
Bob's conscience:
+
Bob's conscience asking...
   
 
<haskell>
 
<haskell>
Line 288: Line 288:
 
> (name,times) = loverDiary (answer times1)
 
> (name,times) = loverDiary (answer times1)
   
> tell something oldtimes = Lover ("", something+oldtimes)
+
> tellLover something oldtimes = Lover ("", something+oldtimes)
> answer newtimes oldtimes = Lover ("",newtimes+oldtimes)
+
> tellMyself something = Lover ("", something)
 
> chLover lover = Lover (lover,0)
   
> tellinside something = Lover ("", something)
 
> answerinside newtimes = Lover ("",newtimes)
 
   
 
</haskell>
 
</haskell>
   
*Main> chain (bob) (chain (askLover (chLover "Cris ") (answer 2)) (askLover (antonia bob) (answer 4)))
+
*Main> askLover (tellMyself 10) (tellLover 1)
  +
Lover {loverDiary = ("",11)}
  +
*Main> askLover (chLover "Lory ") (tellLover 1)
  +
Lover {loverDiary = ("Lory ",1)}
  +
*Main> chain (bob) (chain (askLover (chLover "Cris ") (tellLover 2)) (askLover (antonia bob) (tellLover 4)))
 
Lover {loverDiary = ("Paula Cris Antonia ",11)}
 
Lover {loverDiary = ("Paula Cris Antonia ",11)}
*Main> askLover bob (tell 10)
+
*Main> askLover bob (tellLover 10)
 
Lover {loverDiary = ("Paula ",15)}
 
Lover {loverDiary = ("Paula ",15)}
 
*Main>
 
*Main>
Line 305: Line 308:
   
 
> instance Monad Lover where
 
> instance Monad Lover where
> return a = tellinside a
+
> return a = tellMyself a
 
> m >>= f = askLover m f
 
> m >>= f = askLover m f
   
  +
> whoLoved a = do chLover "Lorenza "
> chLover lover = Lover (lover,0)
 
 
> lorenza <- tellMyself 3
 
> whoLoverLoved a = do chLover "Lorenza "
+
> chLover "Antony "
> lorenza <- answerinside 3
+
> antony <- tellMyself 6
> chLover "Antony "
+
> chLover "Luise "
> antony <- answerinside 6
+
> luise <- tellMyself 2
> chLover "Luise "
+
> tellMyself (lorenza + antony + luise)
> luise <- answerinside 2
 
> return (lorenza + antony + luise)
 
   
 
</haskell>
 
</haskell>
   
*Main> whoLoverLoved bob
+
*Main> whoLoved bob
 
Lover {loverDiary = ("Lorenza Antony Luise ",11)}
 
Lover {loverDiary = ("Lorenza Antony Luise ",11)}
 
*Main>
 
*Main>
   
- [[User:AndreaRossato|AndreaRossato]]
+
- [[User:AndreaRossato|Andrea Rossato]]
   
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]

Revision as of 20:31, 2 September 2006

Meet Bob The Monadic Lover

Note: The source of this page can be used as a Literate Haskel file and can be run with ghci or hugs: so cut paste change and run (in emacs for instance) while reading it...


This is what I have in mind: suppose we have a friend who does very well with females/males. A lot of dating and so on...

Now, we would like to keep track of all his/her ... ehm, affairs...

So let's create a type: we will call it Lover, and its constructor will be Lover.

Here it is:

> newtype Lover a = Lover { loverDiary :: (Name,a) }
>     deriving (Show)
> type Name = String

Very very simple: it is a type where we can store the name of our friend's beloved. Suppose our friend is a male. We will call him Bob.

Ok, now we start playing a bit. But first some useful functions:

 

> createLover name times = Lover (name,times)
> startAffair name (Lover (names,times)) = Lover (name,0)

Indeed we need a way to create a lover, Bob, and start an affair with some beloved.

Very simple: createLover takes a name of a beloved and the number of ... dates.

The other function, startAffair, takes the beloved's name and a lover. The will then substitute the lover's past beloved names with the new beloved's one and remove every track of Bob's previous activities.

Yes, you can say it: she's very jealous of your past, as you can imagine...

You can visualize startAffair as the name of the person you are starting a relationship with:

> jenny = startAffair "Jenny "
> luisa = startAffair "Luisa "
> antonia = startAffair "Antonia "

Well, jenny, luisa and antonia are partial applications. They need a lover!

So now need to create our lover, Bob:

> bob = createLover "Paula " 5

Bob had a previous beloved: Paula. They dated 5 times...

Easy, isn't it?

So let's start playing around:

 *Main> bob
 Lover {loverDiary = ("Paula ",5)}
 *Main> luisa bob
 Lover {loverDiary = ("Luisa ",0)}
 *Main> antonia bob
 Lover {loverDiary = ("Antonia ",0)}
 *Main> 

Obviously when you start an affair you do not just meet once, without doing anything... I mean, Bob is proud of his past record and would like to see the 5 increased!

Instead everytime he starts a new affair he has to pretend, with his lover, this is the first time!

Not what he wants really.

Quite human, I'd say.

Let's give Bob a method to ... well, you know...

> oneMoreTime (Lover (name,times)) = Lover (name,times + 1)

oneMoreTime does what it says: add one more time in Bob's personal record.

Let's try it:

 *Main> oneMoreTime bob
 Lover {loverDiary = ("Paula ",6)}
 *Main> oneMoreTime (antonia bob)
 Lover {loverDiary = ("Antonia ",1)}
 *Main> 


Fine, it works as expected.

Now, Bob is, well, that kind of type that seems not really believe in the "Real Love", if you know what I mean.

He needs one more method:

> changeBeloved newname (Lover (name,times)) = Lover (name ++ newname,times)

changeBeloved is very simple: takes a name of a new beloved and concatenate with the names stored in Bob's loverDiary. Bob is proud of his record, and does not pretend to be a fresh men every time he starts a new reletionship.

What kind of a type is this Bob!

Let's test how our Bob is doing. Remember that he started with Paula and 5 times in his record:

 *Main> oneMoreTime $ oneMoreTime (luisa bob)
 Lover {loverDiary = ("Luisa ",2)}
 *Main> oneMoreTime $ oneMoreTime bob
 Lover {loverDiary = ("Paula ",2)}
 *Main> oneMoreTime $ oneMoreTime $ oneMoreTime $ oneMoreTime (antonia bob)
 Lover {loverDiary = ("Antonia ",4)}
 *Main> oneMoreTime $ oneMoreTime $ oneMoreTime $ changeBeloved "Carla " bob
 Lover {loverDiary = ("Paula Carla ",8)}
 *Main> 

He's doing quite fine, I'd say, if you like this kind of types.

New you can see the different behaviors: (antonia bob) will make Bob forget about Paula, while (changeBeloved "Carla " bob) will not.

I'm sure you know what method Bob likes most. And if I were to be Bob, well, I would agree with him, I must confess.

So we need a new method, for those kind of types like Bob. This methods has to chain affairs without letting those ladies erase our memory! It's just a matter of "male's determination"!

I hate writing this thing, but... let's face reality!

So, there's the (typically masculine) method:

> chainAffairs (Lover (names,oldtimes)) (Lover (newlady,newtimes)) = Lover (newlady++names,newtimes+oldtimes)

This method is very simple: it takes two love affairs, the old one and the new one, and chains them together: the new lady (++) with the old ones, and newtimes + oldtimes!

That's all what Bob wants! And needs!

Let's see if it works:

  *Main> chainAffairs (oneMoreTime $ oneMoreTime (antonia bob)) ( oneMoreTime $ changeBeloved "Carla " bob)
  Lover {loverDiary = ("Antonia Paula Carla ",8)}

Remember where Bob was starting from:

  *Main> bob
  Lover {loverDiary = ("Paula ",5)}
  *Main> 

Now, this is fine, sure. It fits Bob's needs. Still we would like to have some way of stating how many times an affairs resulted in a ... well, we know what we mean.

I'd like to take a quite general solution, because Bob is lazy and sometimes forgets to update his diary. So we would like to be able to write that the number of... times doubled:

> times f (Lover (name,times)) = Lover (name, f times)

Let's see how this function works:

 *Main> chainAffairs (times (+3) (antonia bob)) (times (*2) $ changeBeloved "Carla " bob)
 Lover {loverDiary = ("Paula Carla Antonia ",13)}
 *Main> 

So: Bob started at 5 with Paula, doubled it change Paula for Carla. Then met Antonia, (the real love?) and told her he never did anything bad. We Antonia he did it 3 times.

Remember: when an affair is started by a beloved, like "(antonia bob)", we can only use "+", since antonia pretends Bob to be a freshman!

  *Main> chainAffairs (times (+3) (antonia bob)) (times (*2) (luisa bob))
  Lover {loverDiary = ("Luisa Antonia ",3)}
  *Main> 

instead:

 *Main> chainAffairs (times (+3) (antonia bob)) (times (+2) (luisa bob))
 Lover {loverDiary = ("Luisa Antonia ",5)}
 *Main> 

I'll ask it again: what kind of a type is this Bob!!! A sex intercourses calculating machine!

There's a name for males who just collect ... well ... females: we call them Macho Men!

Like Bob they just chain love affairs, doing just calculations:

> class Macho f where
>     chain :: (Num a) => f a -> f a -> f a

It is plain obvious that a lover of the kind of Bob must be an instance of this class:

> instance Macho Lover where
>     chain (Lover (a,b)) (Lover (c,d)) = (Lover ((a++c),(b+d)))

Do you really want some evidences??

There you are:

 *Main> chain (times (+3) (antonia bob)) (chain (times (*2) (changeBeloved "Carla " bob)) (times (+2) (luisa bob)))
 Lover {loverDiary = ("Antonia Paula Carla Luisa ",15)}
 *Main> 

Look at the way Bob performs his calculation. It is basically mapping its diary with some function. And indeed this is what "times" does.

"times" is just a "map" for Bob's loverDiary.

In Haskell there is a class for this kind of types. I mean, those types that can be mapped as Bob's loverDiary can.

They are called Functors. Pure calculating machine, without any soul left.

Sure Bob is an instance of this class, you can be sure:

> instance Functor Lover where
>     fmap f (Lover (name,times)) = Lover (name, f times)

Do you still want some evidence?

  *Main> chain (fmap (+3) (antonia bob)) (chain (fmap (*2) (changeBeloved "Carla " bob)) (fmap (+2) (luisa bob)))
  Lover {loverDiary = ("Antonia Paula Carla Luisa ",15)}
  *Main> 


to be continued

Bob's conscience asking...

> askLover lover  answer = Lover (name1 ++ name,times)
>     where (name1,times1) = loverDiary lover
>           (name,times) = loverDiary (answer times1)

> tellLover something oldtimes = Lover ("", something+oldtimes)
> tellMyself something = Lover ("", something)
> chLover lover = Lover (lover,0)
 *Main> askLover (tellMyself 10) (tellLover 1)
 Lover {loverDiary = ("",11)}
 *Main> askLover (chLover "Lory ") (tellLover 1)
 Lover {loverDiary = ("Lory ",1)}
 *Main> chain (bob) (chain (askLover (chLover "Cris ") (tellLover 2)) (askLover (antonia bob) (tellLover 4)))
 Lover {loverDiary = ("Paula Cris Antonia ",11)}
 *Main>  askLover bob (tellLover 10) 
 Lover {loverDiary = ("Paula ",15)}
 *Main> 
> instance Monad Lover where
>     return a = tellMyself a
>     m >>= f = askLover m f 

> whoLoved a = do chLover "Lorenza "
>                 lorenza <- tellMyself 3 
>                 chLover "Antony "
>                 antony <- tellMyself 6 
>                 chLover "Luise "
>                 luise <- tellMyself 2
>                 tellMyself (lorenza + antony + luise)
 *Main> whoLoved bob
 Lover {loverDiary = ("Lorenza Antony Luise ",11)}
 *Main> 

- Andrea Rossato