I don't understand why the following code behaves the way it does:
myand :: Bool -> Bool -> Bool
myand True True = True
myand _ _ = False
containsAandB :: String -> IO Bool
containsAandB s = do
containsA <- do
putStrLn $ "Check if 'a' is in " ++ s
return $ 'a' `elem` s
containsB <- do
putStrLn $ "Check if 'b' is in " ++ s
return $ 'b' `elem` s
return $ containsA `myand` containsB
This is the output when I test the function:
*Main> containsAandB "def"
Check if 'a' is in def
Check if 'b' in in def
False
Note that (&&) behaves just like 'myand', I just wrote a custom function to better visualize what's happeninig.
I'm surprised about the 'Check if 'b' is in def
part since containsA
is already false so 'myand' can be evaluated regardless of containsB
.
Question 1:
Is there a particular reason why containsB
has to be evaluated too? My understanding was that the containsB <- do ...
statement is not evaluated unless it's required but my guess is that this might behave differently since it's IO and therefore not free of side effects?
Question2:
What's the best practice approach to get the desired behavior (if containsA
is false, containsB
is not checked) without dealing with nested if-else
clauses?