I don't understand why my haskell program works

{-

-- I'm commenting my remarks Haskell style, so that this whole file, minus

-- the usenet headers, should be acceptable to hugs.

-- This is the function that got me started. Copied from a document on

-- the Internet by Noel Winstanley that tries to explain monads.

-- url is http://www.dcs.gla.ac.uk/~nww/Monad.html

thenMb :: Maybe a -> (a -> Maybe b) -> Maybe b

thenMb x f = case x of

Nothing -> Nothing

Just r1 -> f r1

-- I was trying to play around with thenMb, to better my understanding:

-- tryMaybe is just a function to get things started, a way to produce a

-- value of type Maybe, and control it to be either a (Just string) or Nothing

-- by having the two arguments be equal or not.

tryMaybe :: Int -> Int -> Maybe String

tryMaybe a b | a == b = Just (show a)

| otherwise = Nothing

-- tMs is "to Maybe string", a string to a Maybe String

-- whereas tryMaybe takes Ints, later I want to play with strings only

tMs :: String -> Maybe String

tMs s | (head s == '5') = Just (s ++ s)

| otherwise = Nothing

-- elaborate on Maybe b, here's where I use thenMb:

elabMb :: Int -> Int -> Maybe String

elabMb n1 n2 = (tryMaybe n1 n2) `thenMb` \r1 ->

tMs r1 `thenMb` \r2 ->

tMs r2 -- What I don't understand is why r1 & r2 are treated as String

-- and not as Maybe String

-- tMs accepts String and not Maybe String. when I tried

-- using mTms (which was written to accept a Maybe String)

-- I had to fix up the call (in melabMb, ie maybe elabMb) by

-- making it (Just r1)

-}

--but look at type of

--thenMb :: Maybe a -> (a -> Maybe b) -> Maybe b

--it takes one 'Maybe a' and a function from 'a' to 'Maybe b'

-- ^^^

--in our case it takes (tryMaybe n1 n2) of type 'Maybe String'

--and (\r1 -> tMs r1 ...) of type 'String' to 'Maybe String'

--that is :

--the first arg of thenMb should Maybe return an a

--and the second arg should be a function that takes the a

--and Maybe produces an b

--but if the first arg fails to produce anything

--then the second arg is never called with the a (there is none)

--in our case :

--the part '\r1 -> ...' is a function that takes the String from

--(tryMaybe n1 n2) if it produced any string and then gives ...

--but now if tryMaybe fails then the rest is just skipped

--therefore it is possible for the second arg of thenMb

--to take 'String' and not 'Maybe String'

--clear ?,

--if not try to understand the (rather confusing, i'm afraid)

--reduction below

--stepping through a (long) example :

--? elabMb 58 58

-- (because of elabMb definition)

--> (tryMaybe 58 58) `thenMb` (\r1 -> tMs r1 `thenMb` (\r2 -> tMsr2))

-- (thenMb forces arg 1 =>)

-- (eval (tryMaybe 58 58) =>)

-- (try first guard :)

-- --| 58 == 58

-- --> True

-- (succeeded => (tryMaybe 58 58) --> (Just (show 58)))

--> (Just (show 58)) `thenMb` (\r1 -> tMs r1 `thenMb` (\r2 -> tMs r2))

-- (arg 1 now match with first def. of thenMb)

-- (** here the Just is taken away **)

--> (\r1 -> tMs r1 `thenMb` (\r2 -> tMs r2)) (show 58)

-- (application of (lambda) fun. with arg)

--> tMs (show 58) `thenMb` (\r2 -> tMs r2)

-- (thenMb forces arg 1 =>)

-- (eval tMs (show 58) =>)

-- (try first guard :)

-- --| head (show 58) == '5'

-- -- (head forces show => ... =>)

-- --> head "5" == '5'

-- --> '5' == '5'

-- --> True

-- (succeeded => (tMs (show 3)) --> (Just ("58" ++ "58")))

--> (Just ("58" ++ "58")) `thenMb` (\r2 -> tMs r2)

-- (arg 1 now match with first def. of thenMb)

-- (** here the Just is taken away **)

--> (\r2 -> tMs r2) ("58" ++ "58")

--> tMs ("58" ++ "58")

-- (try first guard :)

-- --| head ("58" ++ "58") == '5'

-- --> head ('5':_) == '5'

-- --> '5' == '5'

-- --> True

-- (succeeds =>)

--> ("58" ++ "58") ++ ("58" ++ "58")

--> "58585858"

--can you comprehend it now, or do you need more (better) explanations ?

{-

mtMs :: Maybe String -> Maybe String

mtMs (Just s) = tMs s

mtMs Nothing = Nothing

melabMb :: Int -> Int -> Maybe String

melabMb n1 n2 = (tryMaybe n1 n2) `thenMb` \r1 ->

mtMs (Just r1) `thenMb` \r2 ->

mtMs (Just r2)

-- I'm also surprised by the robustness. If the two numbers that kick

-- things off aren't equal, tryMaybe produces Nothing, which I presume

-- thenMb fields with a Nothing result that goes to tMs, but tMs wasn't

-- written to cope with a Nothing. Does the Nothing actually trickle

-- down to tMs or doesn't it?

-}

--another function :

--implement the mathematical function

--f(x) = ln (x*(ln x) - 1) + ln (8 - x)

--with maybe and monads

--now ln is log in haskell,

--but it only works for positive numbers (x>0)

--so we make our own logarithm function with maybe

--normal log (= mathematical ln) function

--log :: Float -> Float (actually it is more general)

ln :: Float -> Maybe Float

ln x | x > 0 = Just (log x)

| otherwise = Nothing

--ok?

--now f :

--f(x) = ln (x*(ln x) - 1) + ln (8 - x)

-- ^^^^^^

-- temp1

-- ^^^^^^^^^^^^^^^^^

-- temp2

-- ^^^^^^^^^^

-- temp3

f :: Float -> Maybe Float

f x = ln x `thenMb` \temp1 -> --first calculate ln x and call it temp1

ln (x*temp1 - 1) `thenMb` \temp2 -> --call this temp2

ln (8 - x) `thenMb` \temp3 -> --call this temp3

returnMb (temp2 + temp3) --(can really use Just here)

--so for all the parts that can fail we use thenMb and give it a name

--then if any part fails, the rest of the computation is skipped and

--all Nothing is returned

--now, if one likes to one can use the 'do' syntactic sugar :

f x = do temp1 <- ln x

temp2 <- ln (x*temp1 - 1)

temp3 <- ln (8 - x)

return (temp2 + temp3)

--oh, i almost forgot

returnMb :: a -> Maybe a

returnMb x = Just x

{-

Main> ln 1

Just 0.0

Main> ln 2

Just 0.693147

Main> ln 2.718281828

Just 1.0

Main> ln 10

Just 2.30259

Main> ln 0

Nothing

Main> ln (-6)

Nothing

Main> f 8

Nothing

Main> f 7

Just 2.53539

Main> f 1

Nothing Main> f 2

Just 0.840604

Main> f 5

Just 3.05124

-}

--Stefan Lj