Question
How can the following implementation of fractionParse
be made
less ugly? (Preserving unreduced numerators and denominators is supposed to be a feature---as shown in the second example---so using readMaybe s :: Maybe (Ratio Int)
doesn't really help.)
Things that irked me in the process of writing it, include:
- Not being able to find a way to use do-notation in conjunction with
Maybe
in a way that would return as soon asJust
appears rather than when the firstNothing
appears. In other words aMaybe
monad instance that emulatesor
rather thanand
. case
and pattern matching colliding in unhelpful ways, forcing me to write nestedif-then-else
s.
Usage examples
λ> fractionParse "2/3"
Fraction 2 3
λ> fractionParse "12 / 6"
Fraction 12 6
λ> fractionParse "23"
Whole 23
λ> fractionParse "a"
Rubbish
λ> fractionParse "2/3/4"
Rubbish
λ> fractionParse "2%3"
Rubbish
Implementation
import Text.Read (readMaybe)
import Data.String.Utils (split)
import Data.Maybe (isJust, fromJust)
data FractionParse = Fraction Int Int
| Whole Int
| Rubbish deriving (Show, Eq)
fractionParse :: String -> FractionParse
fractionParse s = let i = maybeInt s
r = maybeNumDenom s in
if isJust r
then let (Just (n,d)) = r in Fraction n d
else if isJust i
then Whole (fromJust i)
else Rubbish
maybeInt :: String -> Maybe Int
maybeInt s = readMaybe s
maybeNumDenom :: String -> Maybe (Int, Int)
maybeNumDenom s = do
if containsExactlyOneSlash s then Just () else Nothing
let [ns,ds] = split "/" s
n <- readMaybe ns
d <- readMaybe ds
return (n,d)
containsExactlyOneSlash s = (length $ filter (=='/') s) == 1