Personal tools

Meet Bob The Monadic Lover

From HaskellWiki

Revision as of 06:53, 3 September 2006 by AndreaRossato (Talk | contribs)

Jump to: navigation, search

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...

So, 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...

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

Let's create a type for that: we'll call it "Lover", and a "Lover" will be its constructor.

There 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 beloveds.

Suppose our friend is a male. We will call him Bob.

Ok, we now start playing a bit. But first some useful functions we are going to need:

> createLover name times = Lover (name,times)
> startAffairWith 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 ... let's call them "dates".

The other function, startAffairWith, takes the beloved's name and a lover. It 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 that: she's a very jealous tupe of chick and hates Bob's past, as you can imagine... Let's face it, Bob is not a saint, after all.

If you want you can visualize "startAffairWith" with the name of the person you are starting a relationship with:

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

As you may see, jenny, luisa and antonia are just partial applications. They need a lover! Well, we know this kind of types, don't we?

Be honest. With yourself, at least!

Now we need to create our lover. So, let's do it the right way.

I'd like to introduce you my friend 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)}

Obviously when you start an affair you do not just meet once, without doing anything... Well, perhaps the first date you just be quit, but in the end...

What I mean is that Bob is quite proud of his past record and would like to see the 5 increased! I can understand him.

Instead everytime he starts a new affair with those "always looking for a lover" chicks, well, he has to pretend it is his first time!

Not what he wants, really.

Quite human, I'd say. Still, he has to.

Why don't we just give Bob a new method to ... well, you know...

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

oneMoreTime does what it says, and doues it well: adds one more time in Bob's personal diary.

Let's see:

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

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.

Indeed 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 it with the names stored in Bob's loverDiary. Bob is proud of his diary, and does not pretend to be a freshman every time he starts a new relationship.

What kind of a type this Bob is!

Let's test how our Bob is doing. Remember that he started with Paula and 5 times in his record, that is not a bad start compared to mine:

 *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)}

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

Now you can alse see Bob's different approaching techniques: "(antonia bob)" will make Bob forget about Paula, while "(changeBeloved "Carla " bob)" will not.

I'm sure you know what method Bob likes most.

Bob is that kind of types who just likes to increase the number of "pieces" in their collection. Their lovers, indeed, are just "pieces".

I mena, if I were to be Bob, well, I would agree with him, I must confess. Luckily I'm not Bob and my beloved seems to appreciate this fact, especially when Bob comes over with a couple of those stupid gorgeous looking chicks. She keeps keeping an eye on me. She doesn't trust me and probably thinks that inside myself there must be some kind of a type like the type of Bob.

Sometimes I even start to believe that she could be right... But now we are talking about Bob, a much more interesting chap the me.

Anyway 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 "lover's self-determination"!

I hate writing these things, 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 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)}

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

I'd like to take a quite general solution here, because our Bob is quite a lazy guy and sometimes, often,he forgets to update his diary, especially those nights he drunk too much. He is not an heavy drinker, far from it, but sometimes...

So we would like to be able to write that the number of times ... doubled. Hard to believe, but you never know with types like Bob:

> 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)}

So: Bob started at 5 with Paula, doubled it when changed Paula for Carla (let me tell you: a good change!). Then he met Antonia, (the real love?) and told her he never did anything bad in his past (I was there and could not refrain from laughing). With Antonia he did it 3 times. Or so he pretends. But I know Antonia and this is probably true. Anyway, never trust what Bob says, ever!

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> chainAffairs (times (+3) (antonia bob)) (times (+2) (luisa bob))
 Lover {loverDiary = ("Luisa Antonia ",5)}

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 pure 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 mychicks = chainAffairs mychicks

Do you really need 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)}

Look at the way Bob performs his calculations. He is basically mapping his diary with some function. And indeed this is what "times" is supposed to do.

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

In Haskell we have a class for this kind of types. I mean, those types who map objects that can be mapped over as Bob's loverDiary can.

I think the name of their class i quite appropriate: they are called Functors. Pure calculating machine, without any soul left.

Bob must be an instance of this class, you can be sure of that:

> instance Functor Lover where
>     fmap f = times f

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)}

to be continued

Bob's conscience asking...

> askLover lover answer = Lover (oldnames ++ newname,newtimes)
>     where (oldnames,oldtimes) = loverDiary lover
>           (newname,newtimes) = loverDiary (answer oldtimes)
> tellLover newtimes oldtimes = Lover ("", newtimes+oldtimes)
> tellMyself newtimes = Lover ("", newtimes)
> newLove love = Lover (love,0)

 *Main> askLover (tellMyself 10) (tellLover 1)
 Lover {loverDiary = ("",11)} -- Leibniz
 *Main> askLover (newLove "Lory ") (tellLover 1)
 Lover {loverDiary = ("Lory ",1)}
 *Main> chain (bob) (chain (askLover (newLove "Cris ") (tellLover 2)) (askLover (antonia bob) (tellLover 4)))
 Lover {loverDiary = ("Paula Cris Antonia ",11)}
 *Main>  askLover bob (tellLover 10) 
 Lover {loverDiary = ("Paula ",15)}

bob last night

 *Main> fmap (*3) chain bob  (askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia" (antonia bob)))) (tellLover 10))
 Lover {loverDiary = ("Paula Antonia ClaudiaBerta Jennyfer L. ",45)}
 *Main> fmap (*3) $ askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia" (antonia bob)))) (tellLover 10)
 Lover {loverDiary = ("Antonia ClaudiaBerta Jennyfer L. ",30)}
 *Main> chain bob $ fmap (*5) (askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia" (antonia bob)))) (tellLover 10))
 Lover {loverDiary = ("Paula Antonia ClaudiaBerta Jennyfer L. ",55)}
> instance Monad Lover where
>     return a = tellMyself a
>     m >>= f = askLover m f 
> whoLovedBy bob = do newLove "Lorenza "
>                     lorenza <- tellMyself 3 
>                     newLove "Antony "
>                     antony <- tellMyself 6 
>                     newLove "Luise "
>                     luise <- tellMyself 2
>                     tellMyself (lorenza + antony + luise)
 *Main> whoLovedBy bob
 Lover {loverDiary = ("Lorenza Antony Luise ",11)}

- Andrea Rossato