Meet Bob The Monadic Lover

From HaskellWiki
Revision as of 11:24, 3 September 2006 by AndreaRossato (talk | contribs) (wikifix)
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)}

Well, is Bob just that? I mean, I know him and he can be the worst kind of a type, I'm totally aware of that. No doubt. But, you know, there are times when Bob and I hang together, and with me he's not just telling bull... you got it.

Sometimes I find him alone in his huge, empty apartment down town, staring at the foll moon and almost crying in despair... Sometimes he opens up with me, and starts telling me whom he really loved. You won't believe it, I'm sure, but one he said that Paula didn't count anything in his life. I've said it all!

Now, I'm sure that when he's alone, Bob talks with himself about himself. He's not just a Macho Functor who sums up story after story as a calculating machine. He judges his lover, I doesn't say "I changed my beloved" or stuff like that. I'm sure he's able to love. Sometimes.

I'd like to be there when Bob talks with his soul. I'd like to hear the soul asking Bob about his lovers. And I'd like to see if in this occasions Bob uses the same methods, with himself.

I'm sure you know what I mean. You to have a soul you talk with. And you know that your soul knows it all. You don't need to talk with her as you would with me. She know part of your answers, because you to guys, whether you want it or not, share the same past, the same memories. This is you, after all. And you know better the I do. Am I right?

I don't want to start doing philosophy. This is not the place. But you mast agree that it would be unjust to believe that Bob is just a Macho Functor. You must concede that Bob is something more. He is a soul talking with Bob's self... Call it ego, call it psyche, call it as you want, but you know perfectly know what we are talking about. Do not lie to yourself!

Ahh, who was that philosopher that said that lying to others is the exception. The rule is lying to ourselves. Friedrich what? I don't remember anymore...

Ok, let's sto with all this bull... ops, I start talking like Bob!

Let's go on an see how we can express this human behavior that Bob seems to have, sometimes.

We said that the soul asks Bob, but that the soul knows part of the answer. So the answer must imply something the soul knows. In other word, the answer must be some kind of partial application, right?

So let's write this first:

> tellLover newtimes oldtimes = Lover ("", newtimes+oldtimes)

And the the question:

> askLover lover answer = Lover (oldnames ++ newname,newtimes)
>     where (oldnames,oldtimes) = loverDiary lover
>           (newname,newtimes) = loverDiary (answer oldtimes)

This seems to be just fine to me. "askLover" takes a partial answer and fills it with the missing part (oldtimes) in the where clause: the soul's memory, extracted from the loverDiary of the lover, memory that Bob and his soul share, is added to the partial answer.

But, we said, this is just what we sees. Bu when Bob's soul is alone with herself, I mean, inside Bob, who is probably sitting somewhere all stoned and drunk, this soul needs some method to talk to herself about new loves and old times.

So, let's write these soul's method:

> tellMyself newtimes = Lover ("", newtimes)
> newLove love = Lover (love,0)

To me that seems to be enough. Well, just give it a try:

 *Main> askLover (tellMyself 10) (tellLover 1)
 Lover {loverDiary = ("",11)}

Well, this for sure is not Bob! Look at this poor soul of a type. 11 dates and no names. Sounds suspicious to me... This guy, all alone, doing what??

I'd have a name for such a "spirit"! Remember that German philosopher who was talking of this stuff all alone, the essence... I don't remember his name but if you look up at the Wikipedia something could come up.

Let's try again:

 *Main> askLover (newLove "Lory ") (tellLover 1)
 Lover {loverDiary = ("Lory ",1)}

Well this is not Bob. I don't know him personally. But he doesn't seem such an interesting guy, judging from numbers (you know, the macho functor stuff).

So here's Bob:

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

Now I recognize him. The old dear Bob. What a type.

Be careful, with this stuff, though. Do not pretend it being the truth about Bob. When Bob tells something aloud to his soul you never know what he's talking about. Truth is not what Bob likes most.

You sure know that by now!

Just to show, this is Bob a couple of weeks ago. We went over to one of his friends, and he started, as usual, talking about what kind of a type of lover he is, showing off names I would not dare to write here.

just a couple of examples:

 *Main> fmap (*3) chain bob  (askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia " (antonia bob)))) (tellLover 10))
 Lover {loverDiary = ("Paula Antonia Claudia Berta Jennyfer L. ",45)}
 *Main> fmap (*3) $ askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia " (antonia bob)))) (tellLover 10)
 Lover {loverDiary = ("Antonia Claudia Berta Jennyfer L. ",30)}
 *Main> chain bob $ fmap (*5) (askLover (changeBeloved "Jennyfer L. " (changeBeloved "Berta " (changeBeloved "Claudia " (antonia bob)))) (tellLover 10))
 Lover {loverDiary = ("Paula Antonia Claudia Berta Jennyfer L. ",55)}

Would you believe it? Certainly I don't. Why? because I happen to know Bob better then you!

Well, all this soul stuff looks interesting. We could have some fun with it.

For sure now we can build females lovers who are not just ... partial application.

Introducing Alessia:

 *Main> let alessia = askLover (newLove "Joseph ") (tellLover 4)
 *Main> alessia
 Lover {loverDiary = ("Joseph ",4)}

And what about the bird of a new soul without any memory and experience.

Sorry, but I find English not expressive enough for what I mean. Well, I'll admit that it could be just me. But how do you guys say when some is going to, you know, be born.. As far as my English goes born is the past participle of bear. This is fine, but I believe its the woman's perspective. She bears what is going to be born.

Ok, let's start from a pregnant woman. I must confess I do not have very much experience in this field, but I would probably express a pregnant like this:

  • Main> askLover (askLover (newLove "Bob ") (tellLover 0)) (tellLover 1)

Lover {loverDiary = ("Bob ",1)}

  • Main>

I don't know if it does make sense. But for sure pregnancy doesn't make sense to Bob. So, possibly, this is the right way to express it. At least when talking about Bob.

But I need a name for this method: the act of the birth. We need a future participle, this is what we really need. You know, something like the Latin "naturum" (is to be born), the Greek physis, the Italian nascita or natura.

Yes, something like "nature" seems to be appropriate. But just to keep apart from the real "nature", like wildness and stuff like that (Bob lives down town and hates everything that even seems "natural"), we will use the Greek term physis.

Also as an homage to that Greek philosopher, Eraclo, Heracloto, I don't remember, that used to say: "Physis kryptestai phylein" (nature loves to hide herself).

> physis = Lover ([],0)

Let's try it:

 *Main> let andrea = physis
 *Main> andrea
 Lover {loverDiary = ("",0)}

Ok, seems to be working. And also the name seems appropriate. Before his birth, he was not. During the evaluation of his "going to be born" became a "definitely born".

And how a Lover dies? This is simple:


> dies lover = Lover ([],0)
 *Main> let alessia = askLover (newLove "Joseph ") (tellLover 4)
 *Main> alessia
 Lover {loverDiary = ("Joseph ",4)}
 *Main> dies alessia
 Lover {loverDiary = ("",0)}

Seems to work to me.

I know what you are going to say now. How can you tell who's just born from who's just died? Well, from our perspective, no differences at all. But from a Macho Functor Lover, well, this is a big loss. take it for sure!

I like playing this game, Haskell let's create quite interesting kinds of types. I was thinking, do you remember that guy in that movie?

How was titled? Memento I think.

This Leonard kind of type, who when falls asleep looses all his short term memory... I'd like to introduce this kind of type. I think this is going to be easy:

> newtype Leonard a = Leonard { leonardDiary :: (Name,a) }
>     deriving (Show)

> askLeonard lover answer = Leonard (oldnames,oldtimes)
>     where (oldnames,oldtimes) = leonardDiary lover
>           (newname,newtimes) = leonardDiary (answer oldtimes)

> leonard = Leonard ("Jorja Fox", 1000)

> tellLeonard newtimes oldtimes = Leonard ("", newtimes+oldtimes)
> tellLeonardSelf newtimes = Leonard ("", newtimes)
> newLeonardLove love = Leonard (love,0)

I believe this is him. Well, just try:

 *Main> askLeonard (leonard) (tellLeonard 100000000)
 Leonard {leonardDiary = ("Jorja Fox",1000)}

Seems to be Leonard to me. Definitely.

What do you think, could this kind of a type be a Macho? I believe he can:

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

Sure he can. Look here:

 *Main> chain leonard (askLeonard (newLeonardLove "Angolie ") (tellLeonard 3))
 Leonard {leonardDiary = ("Jorja Fox",1000)}

This is definitely the Leonard type.

Now I'm asking to myself if Leonard can also be considered a Functor.

yes, I do believe he can:

> instance Functor Leonard where
>     fmap f (Leonard (a,b)) = Leonard (a,f b)

Try him:

 *Main> fmap (+6) (chain leonard (askLeonard (newLeonardLove "Angolie ") (tellLeonard 3)))
 Leonard {leonardDiary = ("Jorja Fox",1006)}

A bit strange, isn't it? Seems like Leonard was thinking to do it with Jorja... Well, perhaps this just because he's awake. Just wait for him to go to sleep and check again. Otherwise, well, my fault!

Ohh gosh! I'm sure now your are starting to believe I'm going to build up a Matrix!

No, I'm not. We'd better go back to our Being John Malchowich style. Right?

to be continued Bob's Soul.

> instance Monad Lover where
>     return a = tellMyself a
>     m >>= f = askLover m f 

Does Leonard have a soul left?
Try it yourself!

instance Monad Leonard where
   return a = tellLeonardSelf a
   m >>= f = askLeonard m f 

That night with Bob

> whoLovedBy bob = do newLove "Andrea "
>                     andrea <- tellMyself 1 
>                     newLove "Bob "
>                     bob <- tellMyself 1
>                     tellMyself (andrea + bob)
 *Main> whoLovedBy bob
 Lover {loverDiary = ("Lorenza Antony Luise ",11)}

No, I was just kidding....

In the very moment I looked inside Bob this is precisely what I saw:

whoLovedBy bob = do newLove "Andrea "

                   andrea <- tellMyself 1 
                   tellMyself (andrea + (whoLovedBy bob))

Can Haskell handle that much?

- Andrea Rossato