In-class-5

Class: CSCE-314

-- 1) Polymorphism: keep only successful results
mapOptional :: (a -> Maybe b) -> [a] -> [b]
mapOptional f xs = [ y | x <- xs, Just y <- [f x] ]
    -- Input: a function 'f :: a -> Maybe b'
        -- Produces 'b' for some 'a' (returns 'Just b')
        -- Fail to produce 'b' (returns Nothing)
    -- Input: a list of 'a' type values
    -- x <- xs iterates each element x of the input list.
    -- Just y <- [f x] is a pattern-match generator
        -- If f x is Just y, the pattern match succeeds (y is added).
        -- If f x is Nothing, the pattern match fails (no y is produced).
    -- Return: "List of all b results for which 'f x' resturned 'Just b'"
        -- 'y' refers to type b elements
        -- Drops elements where 'f' returns Nothing
    -- Usage:
        -- ghci> let f x = if even x then Just (x*2) else Nothing
        -- ghci> mapOptional f [1..6]
        -- [4,8,12]

-- 2) Safe integer parser for a single token using reads
-- Read an Int from a given String
readIntMaybe :: String -> Maybe Int
readIntMaybe s = 
    case reads s of
        [(n,"")] -> Just n
        _        -> Nothing

-- 3) Replace non-digit/non-sign with spaces to separate tokens
-- Keep '-' so negative numbers remain intact.
normalizeDigits :: String -> String
normalizeDigits = map f                 -- No need to input 's' here
    where
        f c                             -- Input str is 'c' (a single character)
          | isDigit c = c               -- Keep digits
          | c == '-' = c                -- Keep minus signs
          | otherwise = ' '             -- Replace everything else with space char

-- 4) Extract integers from messy text
extractInts :: String -> [Int]
extractInts s = mapOptional readIntMaybe (words (normalizeDigits s))
    -- 'words' splits the normalized string into tokens (so -123, 45, etc. remain intact).
        -- basically makes a list out of a string with space-separated words

-- 5) Variants with Maybe and Either
extractIntsMaybe :: String -> Maybe [Int]
extractIntsMaybe s = 
    case extractInts s of
        (x:xs) ->  Just (x:xs)
        []     -> Nothing

extractIntsEither :: String -> Either String [Int]
extractIntsEither s =
    case extractInts s of
        (x:xs) -> Right (x:xs)
        []     -> Left "no integers found"