100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Maybe and Either

How Haskell uses the Maybe and Either types to represent optional values and recoverable errors explicitly, without null or exceptions.

Type SystemBeginner9 min readJul 10, 2026
Analogies

Maybe: Making Absence Explicit

data Maybe a = Nothing | Just a is Haskell's answer to the 'billion-dollar mistake' of null references: instead of every reference type silently being allowed to hold a special null value, a function that might not have a result returns Maybe a, and the type signature itself -- lookup :: Eq k => k -> [(k, v)] -> Maybe v -- documents that absence is a real possibility the caller must handle. Because Nothing and Just x are different constructors of the same sum type, you cannot accidentally treat a Maybe Int as if it were a plain Int; the compiler forces you to pattern match or use a combinator before you can get at the underlying value, eliminating an entire category of null-pointer-style crashes at compile time.

🏏

Cricket analogy: A scorecard's 'not out' status is explicitly recorded rather than left blank and assumed, the same way Maybe Int explicitly forces you to consider the Nothing case rather than silently assuming a value is always there like an unchecked null.

Chaining Computations with Functor and Monad

Because Maybe has a Functor instance, fmap (+1) (Just 5) applies the function inside the Just and produces Just 6, while fmap (+1) Nothing stays Nothing without ever calling the function -- the wrapped context propagates automatically. Maybe's Monad instance takes this further: >>= (bind) lets you chain a sequence of operations that each might fail, short-circuiting to Nothing the moment any one of them does, and do-notation makes that chain read like ordinary sequential code, do { x <- safeDivide 10 2; y <- safeDivide x 0; return y } evaluating to Nothing as soon as the second division by zero occurs, without any explicit branching or exception handling written by hand.

🏏

Cricket analogy: A DRS review chain -- checking the no-ball, then the bat edge, then the pitching line -- stops the instant any check fails and returns 'not out', mirroring how >>= chains Maybe computations that short-circuit to Nothing the moment one step fails.

haskell
safeDivide :: Int -> Int -> Maybe Int
safeDivide _ 0 = Nothing
safeDivide x y = Just (x `div` y)

-- Chaining with do-notation: short-circuits on the first Nothing
compute :: Int -> Int -> Int -> Maybe Int
compute a b c = do
  x <- safeDivide a b
  y <- safeDivide x c
  return (y + 1)

-- Either carries error information instead of just absence
data ParseError = EmptyInput | NotANumber String
  deriving Show

parseAge :: String -> Either ParseError Int
parseAge ""  = Left EmptyInput
parseAge str = case reads str of
  [(n, "")] -> Right n
  _         -> Left (NotANumber str)

main :: IO ()
main = do
  print (compute 100 5 2)      -- Just 11
  print (compute 100 0 2)      -- Nothing
  print (parseAge "42")        -- Right 42
  print (parseAge "abc")       -- Left (NotANumber "abc")

Either: Carrying Error Information

data Either a b = Left a | Right b looks structurally similar to Maybe, but where Maybe only tells you a computation failed, Either lets you say why: by strong convention, Left carries error information (like a ParseError or a String message) and Right carries the successful result, a mnemonic reinforced by 'Right' also meaning 'correct'. Either e has a Monad instance just like Maybe does, so a chain of Either-returning operations short-circuits on the first Left exactly the way a Maybe chain short-circuits on the first Nothing -- except now the value that comes out the other end tells you specifically which step failed and why, rather than just that something did.

🏏

Cricket analogy: A match report that records not just 'rain-affected' but specifically 'abandoned due to rain in the 34th over with no result achievable' carries far more information than a bare 'no result' flag, mirroring how Left (NotANumber "abc") carries a specific reason, unlike Maybe's bare Nothing.

Practical Helpers: maybe, either, fromMaybe

Rather than manually pattern matching every time, Data.Maybe and Data.Either provide small combinator functions that consume these types safely: maybe :: b -> (a -> b) -> Maybe a -> b takes a default value, a function to apply if there's a Just, and the Maybe itself, collapsing it to a plain value in one call; fromMaybe :: a -> Maybe a -> a is the common special case that just supplies a default; and either :: (a -> c) -> (b -> c) -> Either a b -> c does the analogous job for Either, taking one function for the Left case and one for the Right case. catMaybes :: [Maybe a] -> [a] is handy for filtering a list of Maybe values down to just the successes, discarding every Nothing in one pass.

🏏

Cricket analogy: A scorecard summary tool that takes 'if not out, show N/A; if out, show the dismissal type' is a single reusable rule rather than writing custom branching for every player, mirroring how maybe collapses a Maybe to a plain value with one reusable default-plus-function call.

Data.Maybe.mapMaybe :: (a -> Maybe b) -> [a] -> [b] combines mapping and filtering in one pass -- it's equivalent to catMaybes . map f but avoids building the intermediate list of Maybe values, making it the idiomatic choice whenever you need to both transform and filter a list based on a function that can fail.

Data.Maybe.fromJust :: Maybe a -> a and pattern matches that only handle the Just case (leaving Nothing to error "..." or an incomplete pattern) throw away the entire safety guarantee Maybe exists to provide -- reaching for fromJust is usually a sign the function should either return Maybe itself, or that the calling code should use maybe/fromMaybe/pattern matching to handle the Nothing case honestly instead of asserting it can't happen.

  • Maybe a = Nothing | Just a replaces null references, making the possibility of absence part of the type signature itself.
  • Maybe's Functor instance (fmap) applies a function inside a Just and no-ops on Nothing; its Monad instance (>>=, do-notation) chains fallible computations, short-circuiting on the first Nothing.
  • Either a b = Left a | Right b is like Maybe but the failure case (Left) carries specific error information instead of a bare absence.
  • By convention, Left holds an error/failure value and Right holds a success value; Either's Monad instance short-circuits on the first Left.
  • maybe, fromMaybe, and either are combinator functions that collapse Maybe/Either values without manual pattern matching.
  • catMaybes and mapMaybe filter/transform lists of Maybe values in one pass, discarding Nothings.
  • Reaching for partial functions like fromJust defeats the safety Maybe provides and is usually a sign the Nothing case should be handled explicitly instead.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#HaskellStudyNotes#MaybeAndEither#Maybe#Either#Making#Absence#StudyNotes#SkillVeris#ExamPrep