Map over the optional payload, preserving failure:This is the optional-value mapping operation, equivalent to
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe _ Nothing = Nothing
mapMaybe f (Just x) = Just (f x)fmap for Maybe. The question's local name is distinct from the library function Data.Maybe.mapMaybe, which filters and maps an entire list. The algebraic data type keeps absence separate from every ordinary payload value.The most general type signature is
foldMaybe :: (a -> b) -> b -> Maybe a -> b. The Nothing branch returns the default of type b; the Just branch applies the supplied function to an a, also producing a b.Separate the maximal decimal prefix from the unconsumed suffix, rejecting an empty prefix:The left fold uses the positional notation recurrence . This parses unsigned decimal operands as illustrated, leaving the first nondigit and everything after it untouched. As the required result type is
import Data.Char (isDigit, digitToInt)
type Parse a = String -> Maybe (a,String)
parseInt :: Parse Int
parseInt input =
case span isDigit input of
([],_) -> Nothing
(ds,ys) -> Just (foldl (\n d -> 10*n + digitToInt d) 0 ds, ys)Int, correct mathematical values require the parsed integer to fit that bounded type; arbitrary-length exact values would instead need Integer or an explicit integer overflow check.The parser recognizes exactly one leading operator character:An empty input fails, and any later characters remain for the next parser. The last example printed in this part uses the name
parseOp :: Parse Char
parseOp (c:cs)
| c `elem` "+-*/" = Just (c,cs)
parseOp _ = NothingparseInt; its failure result does not change the requested operator parser.Compose the three parsing stages using the supplied fold, and accept only when no suffix remains:Every failed intermediate parser is converted to
eval :: String -> Maybe Int
eval input = foldMaybe first Nothing (parseInt input)
where
first (a,rest) = foldMaybe (second a) Nothing (parseOp rest)
second a (op,rest) = foldMaybe (finish a op) Nothing (parseInt rest)
finish a op (b,rest)
| null rest = Just (apply op a b)
| otherwise = Nothing
apply '+' a b = a+b
apply '-' a b = a-b
apply '*' a b = a*b
apply '/' a b = a `div` b
apply _ _ _ = error "unreachable: parseOp rejected this operator"Nothing by foldMaybe. Checking null rest rejects both a second operation and trailing letters. In particular, integer division makes eval "34/4" = Just 8. As permitted by the question, division by zero is outside the specified inputs; the bounded-Int proviso also applies to arithmetic results. Articles by others on the same topic
There are currently no matching articles.