[Haskell] Mixing monadic and non-monadic functions

Graham Klyne GK at ninebynine.org
Tue Mar 23 16:36:51 EST 2004


I think you're a rather stuck with the "temporary variables" (which they're 
not really), but it might be possible to hide some of the untidiness in an 
auxiliary monadic function.

Assuming this function is given:

assertBool :: String -> Bool -> IO ()

...

My first stab would be:

assertBool1 :: IO [a] -> IO [a] -> ([a] -> [a] -> Bool) -> IO ()
assertBool1 f1 f2 comp = do
         a1 <- f1
         a2 <- f2
         assertBool "fail" $ comp a1 a2

Then your main code could be:

         assertBool1 (someFunc a) (someFunc b) (==)

...

So far so good, maybe?  But what happens if the values you want to test are 
not lists?  The assertBool1 could be generalized somewhat:

assertBool2 :: IO a -> IO b -> (a -> b -> Bool) -> IO ()
assertBool2 fa fb comp = do
         va <- fa
         vb <- fb
         assertBool "fail" $ comp va vb

(All that's really changed here is the type signature)

...

But what if you want a test that uses just one value, or three, or 
more?  At this point I think you start having to use the liftM variants, 
but others may have better ideas.  It may be that the lifting can be hidden 
in auxiliiarty function/operator definitions;  e.g. see the Haskell library 
function Monad.ap for possible clues.

...

Anyway, here's some complete code, tested under Hugs:
[[
import Monad( unless )

assertBool :: String -> Bool -> IO ()
assertBool err bool = unless bool (error err)

someFunc :: String -> IO String
someFunc s = return s

assertBool1 :: IO [a] -> IO [a] -> ([a] -> [a] -> Bool) -> IO ()
assertBool1 f1 f2 comp = do
         a1 <- f1
         a2 <- f2
         assertBool "fail" $ comp a1 a2

test1 :: String -> String -> IO ()
test1 a b =
     do  { assertBool1 (someFunc a) (someFunc b) (==)
         ; putStrLn "test1 OK"
         }
-- test1 "a" "a" -> "test1 OK"
-- test1 "a" "b" -> "fail"

assertBool2 :: IO a -> IO b -> (a -> b -> Bool) -> IO ()
assertBool2 fa fb comp = do
         va <- fa
         vb <- fb
         assertBool "fail" $ comp va vb

test2 :: String -> String -> IO ()
test2 a b =
     do  { assertBool1 (someFunc a) (someFunc b) (==)
         ; putStrLn "test2 OK"
         }
-- test2 "a" "a" -> "test2 OK"
-- test2 "a" "b" -> "fail"
]]

#g
--

At 10:29 23/03/04 -0500, Sean E. Russell wrote:
>Hello,
>
>I posted this question to comp.lang.functional, and someone suggested that I
>try this group instead.
>
>I'm struggling with monads.  Well, not monads themselves, but mixing them 
>with
>non-monadic functions.
>
>Here's my base case:
>
>         someFunc :: String -> IO [a]
>         ...
>                 ax <- someFunc a
>                 bx <- someFunc b
>                 assertBool "fail" $ length ax == length bx
>
>I don't like the assignments; the typing is redundant, if I have a lot of
>asserts like this, and the "variables" are temporary.  What I'd much rather
>have is:
>
>         ...
>                 assertBool "fail" $ (length $ someFunc a) == (length $ 
> someFunc b)
>
>which is more readable, to my eye.
>
>The only solution which has been suggested that may work is liberal use of 
>the
>liftM variants, but this gets *really* tedious and obtuse.
>
>Is there an elegant way to do what I want to do, or am I stuck with
>procedural-style assignments and bunches of temp vars?
>
>Thanks!
>
>--
>### SER
>### Deutsch|Esperanto|Francaise|Linux|XML|Java|Ruby|Aikido
>### http://www.germane-software.com/~ser  jabber.com:ser  ICQ:83578737
>### GPG: http://www.germane-software.com/~ser/Security/ser_public.gpg
>_______________________________________________
>Haskell mailing list
>Haskell at haskell.org
>http://www.haskell.org/mailman/listinfo/haskell

------------
Graham Klyne
For email:
http://www.ninebynine.org/#Contact



More information about the Haskell mailing list