Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I handle Nothing branch of maybe monad gracefully?

data Union = F Float | I Int | S String

getUnionFromString :: String -> Union
getUnionFromString str =
    case (readMaybe str :: Maybe Int) of
        Just i  -> I i
        Nothing ->
            case (readMaybe str :: Maybe Float) of
                Just f  -> F f
                Nothing -> S str

getStr (F f) = "Float " ++ show f
getStr (I i) = "Int " ++ show i
getStr (S s) = "String " ++ s

main = do
    str <- getLine
    putStrLn $ getStr (getUnionFromString str)

I want to make a function that transforms string to union type(Float | Int | String). So I used readMaybe to find out type of a string. And then, using case-of to handle Just and Nothing branch. I think it's not able to cope this situation with Monad behavior of Maybe, because I don't want to stop computation when Nothing got out, I want to handle Nothing case. Is there better way to write getUnionFromString function?

like image 722
lunuy lunuy Avatar asked Nov 16 '25 23:11

lunuy lunuy


1 Answers

We can use Control.Applicative.<|> to simplify the logic of trying a different expression if one evaluates to Nothing:

Prelude> import Control.Applicative
Prelude Control.Applicative> Just 1 <|> Nothing
Just 1
Prelude Control.Applicative> Just 1 <|> Just 2
Just 1
Prelude Control.Applicative> Nothing <|> Just 1
Just 1
Prelude Control.Applicative> Nothing <|> Nothing
Nothing

Using this along with Data.Maybe.fromMaybe, we can simplify the code to:

getUnionFromString :: String -> Union
getUnionFromString str = fromMaybe (S str) $ (I <$> readMaybe str) <|> (F <$> readMaybe str)

Haskell's type inference is able to figure out that the first readMaybe wants a Maybe Int and the second one wants Maybe Float, without us explicitly asking for it.

Full code:

import Control.Applicative
import Data.Maybe (fromMaybe)
import Text.Read

data Union = F Float | I Int | S String

getUnionFromString :: String -> Union
getUnionFromString str = fromMaybe (S str) $ (I <$> readMaybe str) <|> (F <$> readMaybe str)

getStr (F f) = "Float " ++ show f
getStr (I i) = "Int " ++ show i
getStr (S s) = "String " ++ s

main = do
  str <- getLine
  putStrLn $ getStr (getUnionFromString str)
like image 122
Dogbert Avatar answered Nov 18 '25 21:11

Dogbert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!