How to make it better? "Maybe Bool" doesn't look great

Viewed 277

First of all, I'm a novice in Haskell, so if I'm asking somewhat silly question please tell me how can I do better. Thanks :)

My task is to get a list of strings, conforming to specific condition. If no strings conform, I'd like to evaluate to Nothing. So I write two functions:

isSpecialLine :: String -> String -> Maybe Bool
isSpecialLine t s = Just $ (("[" ++ t ++ ":") `isPrefixOf` s) && ("]::" `isSuffixOf` s)

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = filterM (isSpecialLine t) . lines

This code works, but I find that Maybe Bool looks a bit weird. Hey, it's binary! It's always True or False, so value of isSpecialLine will always be Just True or Just False. It can never be Nothing in my case!

But if I change isSpecialLine output type to Bool, following problem appears: filterM expects Maybe Bool instead of Bool.

Ok, so I do that:

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = filterM (Just $ isSpecialLine t) . lines

Now compiler complains about unmatching type: Maybe (String -> Bool) doesn't match expected String -> Maybe Bool. Ok, pretty reasonable. So I:

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = Just $ filter (isSpecialLine t) . lines

And again type mismatching, this time Maybe (String -> [String]) is not String -> Maybe [String]. What's the correct syntax to wrap [String] to Maybe monad?

3 Answers

isSpecialLine never produces Nothing, so that’s a good hint that Maybe might not make sense for it either.

isSpecialLine :: String -> String -> Bool
isSpecialLine t s = (("[" ++ t ++ ":") `isPrefixOf` s) && ("]::" `isSuffixOf` s)

getLinesWith :: String -> String -> [String]
getLinesWith t = filter (isSpecialLine t) . lines

It looks to me that you do not need a filterM here. You can make use of filter :: (a -> Bool) -> [a] -> [a] instead:

isSpecialLine :: String -> String -> Bool
isSpecialLine t s = isPrefixOf ("[" ++ t ++ ":") s && isSuffixOf "]::" s

getLinesWith :: String -> String -> [String]
getLinesWith t = filter (isSpecialLine t) . lines

Indeed, this will return a list of Strings, where each string that matches the isSpecialLine filter will be retained.

Instead of using Maybe and unwrap data from getLinesWith like this,

getLinesWith :: String -> String -> Maybe [String]
...

getDate :: String -> String -> UTCTime
getDate t s =
    timeFromString
    . filter (/= ' ')
    . getBetweenColons
    . maybe ":01-01-1971:" head $ getLinesWith t s

I finished with much better looking (I hope so) solution without Maybe:

getLinesWith :: String -> String -> [String]
...

getDate :: String -> String -> UTCTime
getDate t s
  | null date = timeFromString "01-01-1971"
  | otherwise = timeFromString
              . filter (/= ' ')
              . getBetweenColons
              . head $ date
  where date = getLinesWith t s    

Actually, all the dancing around Maybe began when GHC complained about head []. Thank you all for input!

Related