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?
CodePudding user response:
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)