-- In this log, we explored the uses of Maybe in GHC's Data.List -- https://downloads.haskell.org/~ghc/7.6.2/docs/html/libraries/base/src/Data-List.html -- -- Data.List functions make use of Maybe for their return types to -- be able to return Nothing rather than cause an exception; -- this special value Nothing can be handled down the line with -- code autogenerated by the "monadic" >>= function/operator. -- >>= allows the user of Maybe to write the code in a convenient pipeline, -- which knows to pass Nothing along safely, and write code almost as -- if all the functions were evaluated without the possibility of error, -- with whatever original types of values were wrapped by Maybe. -- -- The only caveat is that each stage in the pipeline must produce not -- the original type, but wrap that in the Just constructor. That is -- the price of using type inference and pattern matching: if Nothing -- has to be added to the original type, pattern matching must have -- a tag to match it simply, as an alternative to Nothing: Prelude> :i Maybe data Maybe a = Nothing | Just a -- Defined in ‘Data.Maybe’ instance Eq a => Eq (Maybe a) -- Defined in ‘Data.Maybe’ instance Monad Maybe -- Defined in ‘Data.Maybe’ instance Functor Maybe -- Defined in ‘Data.Maybe’ instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’ instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’ instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’ -- This does not work. Just is needed to write patterns! Prelude> let { squareamaybe Nothing = Nothing ; squareamaybe x = x * x } -- Notice the inferred type. The Num constraint on Maybe a (rather than a) is probably not what anyone wants. Prelude> :t squareamaybe squareamaybe :: Num (Maybe a) => Maybe a -> Maybe a -- So this fails: Prelude> squareamaybe 10 :73:1: No instance for (Num (Maybe a0)) arising from a use of ‘it’ In a stmt of an interactive GHCi command: print it -- Let's redefine it properly: Prelude> let { squareamaybe Nothing = Nothing ; squareamaybe (Just x) = Just (x * x) } Prelude> :t squareamaybe squareamaybe :: Num a => Maybe a -> Maybe a -- The type above is right, and it works, too: Prelude> squareamaybe Nothing Nothing Prelude> squareamaybe (Just 10) Just 100 -- Oops, the module Data.Maybe that gives us full set of Maybe-handling functions wasn't loaded: Prelude> isJust (Just 10) :77:1: Not in scope: ‘isJust’ -- Loading it & trying these functions out (see code of Data.Maybe in hackage -- repo above): Prelude> import Data.Maybe Prelude Data.Maybe> isJust (Just 10) True Prelude Data.Maybe> fromJust (Just 10) 10 Prelude Data.Maybe> maybeToList (Just 10) [10] -- Some list functions give exceptions: Prelude Data.Maybe> head [] *** Exception: Prelude.head: empty list Prelude Data.Maybe> tail [] *** Exception: Prelude.tail: empty list -- But others are safe, and use Maybe instead of exceptions: Prelude Data.Maybe> import Data.List Prelude Data.Maybe Data.List> stripPrefix "foo" "foobar" Just "bar" Prelude Data.Maybe Data.List> stripPrefix "foo" "foo" Just "" Prelude Data.Maybe Data.List> stripPrefix "foo" "barfoo" Nothing Prelude Data.Maybe Data.List> elemIndex "x" ["y" , "x", "z" ] Just 1 Prelude Data.Maybe Data.List> elemIndex "y" ["y" , "x", "z" ] Just 0 Prelude Data.Maybe Data.List> elemIndex "z" ["y" , "x", "z" ] Just 2 Prelude Data.Maybe Data.List> elemIndex "t" ["y" , "x", "z" ] Nothing -- Let's build a pipeline of function calls that does some arithmetic -- on a found index. It works when the index is successfully found: Prelude Data.Maybe Data.List> (*) 2 $ (+) 1 (fromJust (elemIndex "y" ["y" , "x", "z" ])) 2 Prelude Data.Maybe Data.List> (*) 2 $ (+) 1 (fromJust (elemIndex "x" ["y" , "x", "z" ])) 4 Prelude Data.Maybe Data.List> (*) 2 $ (+) 1 (fromJust (elemIndex "z" ["y" , "x", "z" ])) 6 -- But error out when not: Prelude Data.Maybe Data.List> (*) 2 $ (+) 1 (fromJust (elemIndex "t" ["y" , "x", "z" ])) *** Exception: Maybe.fromJust: Nothing -- Instead, let's use >>= to write an equivalent pipeline that can handle both valid -- indexes *and* Nothing. Note that we write as if Nothing is handled automagically, -- as indeed it is: Prelude Data.Maybe Data.List> elemIndex "t" ["y" , "x", "z" ] >>= \ x -> Just (x+1) >>= \x -> Just (x*2) Nothing Prelude Data.Maybe Data.List> elemIndex "y" ["y" , "x", "z" ] >>= \ x -> Just (x+1) >>= \x -> Just (x*2) Just 2 Prelude Data.Maybe Data.List> elemIndex "x" ["y" , "x", "z" ] >>= \ x -> Just (x+1) >>= \x -> Just (x*2) Just 4 Prelude Data.Maybe Data.List> elemIndex "z" ["y" , "x", "z" ] >>= \ x -> Just (x+1) >>= \x -> Just (x*2) Just 6 -- The same thing, but more succinctly, without lambdas, by using the . composition operator -- Recall that (f . g) x is (f (g x)) --check the type signatures with :t to convince yourself! Prelude Data.Maybe Data.List> elemIndex "x" ["y" , "x", "z" ] >>= Just . ((+) 1) >>= Just . ((*) 2) Just 4 -- Actually, I forgot to kill the lambdas, and got this type error: Prelude Data.Maybe Data.List> elemIndex "a" ["y" , "x", "z" ] >>= \ x -> Just . ((+) 1) >>= \x -> Just (x*2) :109:44: Couldn't match expected type ‘Maybe b’ with actual type ‘b0 -> Maybe b0’ Relevant bindings include it :: Maybe b (bound at :109:1) In the first argument of ‘(>>=)’, namely ‘Just . ((+) 1)’ In the expression: Just . ((+) 1) >>= \ x -> Just (x * 2) In the second argument of ‘(>>=)’, namely ‘\ x -> Just . ((+) 1) >>= \ x -> Just (x * 2)’ -- But I fixed that typo, and it works: Prelude Data.Maybe Data.List> elemIndex "a" ["y" , "x", "z" ] >>= Just . ((+) 1) >>= Just . ((*) 2) Nothing Prelude Data.Maybe Data.List> elemIndex "z" ["y" , "x", "z" ] >>= Just . ((+) 1) >>= Just . ((*) 2) Just 6 --Suggestion: look at how "filter" from GHC.List and "find" from Data.List -- work together. The latter uses Maybe for safe reporting of failure: Prelude Data.Maybe Data.List> :i filter filter :: (a -> Bool) -> [a] -> [a] -- Defined in ‘GHC.List’ Prelude> import Data.List Prelude Data.List> :i find find :: (a -> Bool) -> [a] -> Maybe a -- Defined in ‘Data.List’