Skip to content

Instantly share code, notes, and snippets.

@fedesilva
Forked from jkachmar/Main.hs
Created November 15, 2017 11:06
Show Gist options
  • Save fedesilva/3cd3fecbd05dc21b2dc784f4c6c02db7 to your computer and use it in GitHub Desktop.
Save fedesilva/3cd3fecbd05dc21b2dc784f4c6c02db7 to your computer and use it in GitHub Desktop.
Just Do the Right Thing
module Main where
--------------------------------------------------------------------------------
-- | This is the `Maybe` data type:
-- |
-- | > data Maybe a
-- | > = Nothing
-- | > | Just a
-- |
-- | `Maybe` is a sum type that can be parameterized over a given `a` type, such
-- | that values of type `Maybe a` can be one of `Nothing` or `Just a`.
-- |
-- | In Haskell, `Maybe` typically takes the place of `null` or `undefined` in
-- | other languages. This lets us express that a function _may_ return a value
-- | (or it may not), and forces us to deal with this indecision explicitly.
-- |
-- | Below, we'll use `Maybe`'s `Monad` instance (in the form of Haskell's
-- | do-notation) to write imperative functions that operate over values which
-- | may-or-may-not exist.
-- |
-- | Whenever the function binds a `Just` value to a variable name, it will
-- | continue execution, ultimately wrapping the return value in a `Just` if no
-- | `Nothing`s are encountered.
-- |
-- | If, however, the function attempts to bind a `Nothing` to a variable name,
-- | it will short-circuit and immediately return `Nothing.`
-- | "Succeed" by returning the sume of the value bound out from the `Just` and
-- | a plain number, implicitly wrapping the result in the `Just` data type.
maybeEx1 :: Maybe Integer
maybeEx1 = do
thing1 <- Just 21
let thing2 = 21
return (thing1 + thing2)
-- | "Fail" by attempting to bind out the `Nothing`, thereby returning a
-- | `Nothing`.
maybeEx2 :: Maybe Integer
maybeEx2 = do
thing1 <- Nothing
let thing2 = 21
return (thing1 + thing2)
-- | Attempt to bind out the `Maybe Integer` argument and add it to a plain
-- | number, returning either a `Nothing` (if the argument is a `Nothing`) or
-- | the sum, implicitly wrapped in the `Just` constructor.
maybeEx3 :: Maybe Integer -> Maybe Integer
maybeEx3 maybeNumber = do
thing1 <- maybeNumber
let thing2 = 21
return (thing1 + thing2)
--------------------------------------------------------------------------------
-- | This is the `Either` data type:
-- |
-- | > data Either e a
-- | > = Left e
-- | > | Right a
-- |
-- | `Either` is a sum type that can be parameterized over any `e` and `a`
-- | types, such that values of type `Either e a` can be one of `Left e` or
-- | `Right a`.
-- |
-- | `Either` is like a more descriptive `Maybe`, where typically values
-- | indicating "success" are wrapped in `Right`s and values indicating
-- | "failure" wrapped in `Left`s.
-- |
-- | Below, we'll use `Either`'s `Monad` instance (in the form of Haskell's
-- | do-notation) to write imperative functions that operate over values which
-- | may "succeed" (i.e. are `Right`) or "fail" (i.e. are `Left`).
-- |
-- | Whenever the function binds a `Right` value to a variable name, it will
-- | continue execution, ultimately wrapping the return value in a `Right` if
-- | no `Left`s are encountered.
-- |
-- | If, however, the function attempts to bind a `Left` value to a variable
-- | name, it will short-circuit immediately and return that `Left` value.
-- | "Succeed" by returning the the sum of the value bound out from the `Right`
-- | and a plain number, implicitly wrapping the result in the `Right`
-- | constructor.
eitherStringEx1 :: Either String Integer
eitherStringEx1 = do
thing1 <- Right 21
let thing2 = 21
return (thing1 + thing2)
-- | "Fail" by returning a `Left "Oops, no numbers here!"` value.
eitherStringEx2 :: Either String Integer
eitherStringEx2 = do
thing1 <- Left "Oops, no numbers here!"
let thing2 = 21
return (thing1 + thing2)
-- | Attempt to bind out the `Either String Integer` argument and add it to a
-- | plain number, returning either a `Left` value (if the argument is a
-- | `Left`), or the sum, implicitly wrapped in the `Right` constructor.
eitherStringEx3 :: Either String Integer -> Either String Integer
eitherStringEx3 eitherNumber = do
thing1 <- eitherNumber
let thing2 = 21
return (thing1 + thing2)
--------------------------------------------------------------------------------
-- | This is a `CustomError` sum type.
-- |
-- | Any value with the type `CustomError` can take one of the three forms
-- | provided below:
-- |
-- | `AnError` - a type indicating an error.
-- | `AnotherError` - a type indicating a different error.
-- | `AnErrorWithContext String` - a type that wraps a `String`, letting us tag
-- | values of this type with a descriptive message.
data CustomError
= AnError
| AnotherError
| AnErrorWithContext String
deriving Show
-- | "Succeed" by returning the sum of the value bound out from the `Right`
-- | and a plain number, implicitly wrapped in the `Right` constructor.
eitherCustomEx1 :: Either CustomError Integer
eitherCustomEx1 = do
thing1 <- Right 21
let thing2 = 21
return (thing1 + thing2)
-- | "Fail" by returning a `Left AnError` value.
eitherCustomEx2 :: Either CustomError Integer
eitherCustomEx2 = do
thing1 <- Left AnError
let thing2 = 21
return (thing1 + thing2)
-- | "Fail" by returning a `Left AnotherError` value.
eitherCustomEx3 :: Either CustomError Integer
eitherCustomEx3 = do
thing1 <- Left AnotherError
let thing2 = 21
return (thing1 + thing2)
-- | "Fail" by returning a tagged `Left (AnErrorWithContext "Tag, you're it!")`
-- | value.
eitherCustomEx4 :: Either CustomError Integer
eitherCustomEx4 = do
thing1 <- Left (AnErrorWithContext "Tag, you're it!")
let thing2 = 21
return (thing1 + thing2)
-- | "Succeed" by returning a sum of both numbers bound out from the two
-- | `Right`s, implicitly wrapped in the `Right` data type.
eitherCustomEx5 :: Either CustomError Integer
eitherCustomEx5 = do
thing1 <- Right 21
thing2 <- Right 21
return (thing1 + thing2)
-- | "Fail" by returning a `Left AnError` value.
eitherCustomEx6 :: Either CustomError Integer
eitherCustomEx6 = do
thing1 <- Right 21
thing2 <- Left AnError
return (thing1 + thing2)
-- | Short-circuit the computation by returning the first `Left` value.
eitherCustomEx7 :: Either CustomError Integer
eitherCustomEx7 = do
thing1 <- Left (AnErrorWithContext "Tag, you're it!")
thing2 <- Left AnError
return (thing1 + thing2)
--------------------------------------------------------------------------------
-- | Drop the `Left` branch of an `Either`, demoting it to a `Maybe`.
hush :: Either e a -> Maybe a
hush = either (const Nothing) Just
-- | Annotate the `Nothing` branch of a `Maybe`, raising it to an `Either`.
note :: e -> Maybe a -> Either e a
note e = maybe (Left e) Right
-- | Tag a `Just 21` value with a `CustomError` message, turning it into
-- | `Right 21`.
maybeToEitherEx1 :: Either CustomError Integer
maybeToEitherEx1 = do
let err = AnErrorWithContext "I couldn't find a number in this Maybe!"
thing1 <- note err (Just 21)
let thing2 = 21
return (thing1 + thing2)
-- | Tag a `Nothing` value with a `CustomError` message, turning it into
-- | `Left CustomError`.
maybeToEitherEx2 :: Either CustomError Integer
maybeToEitherEx2 = do
let err = AnErrorWithContext "I couldn't find a number in this Maybe!"
thing1 <- note err Nothing
let thing2 = 21
return (thing1 + thing2)
-- | Drop the error message from a `Right 21` value, turning it into `Just 21`.
eitherToMaybeEx1 :: Maybe Integer
eitherToMaybeEx1 = do
thing1 <- hush (Right 21)
let thing2 = 21
return (thing1 + thing2)
-- | Drop the error message from a `Left AnError` value, turning it into
-- | `Nothing`.
eitherToMaybeEx2 :: Maybe Integer
eitherToMaybeEx2 = do
thing1 <- hush (Left AnError)
let thing2 = 21
return (thing1 + thing2)
--------------------------------------------------------------------------------
-- | Run all of our examples.
main :: IO ()
main = do
------------------------------------------------------------------------------
putStrLn "Some examples with Maybe:"
putStr "maybeEx1: "
print maybeEx1 -- > Just 42
putStr "maybeEx2: "
print maybeEx2 -- > Nothing
putStr "maybeEx3: "
print (maybeEx3 (Just 21)) -- Just 42
putStr "maybeEx3: "
print (maybeEx3 Nothing) -- Nothing
------------------------------------------------------------------------------
putStrLn "\nSome examples with Either and a String error message:"
putStr "eitherStringEx1: "
print eitherStringEx1 -- > Right 42
putStr "eitherStringEx2: "
print eitherStringEx2 -- > Left "Oops, no numbers here!"
putStr "eitherStringEx3: "
print (eitherStringEx3 (Right 21)) -- Right 42
putStr "eitherStringEx3: "
print (eitherStringEx3 (Left "Oops, no number here!")) -- Left "Oops, no numbers here!"
------------------------------------------------------------------------------
putStrLn "\nSome examples with Either and a custom datatype error message:"
putStr "eitherCustomEx1: "
print eitherCustomEx1 -- Right 42
putStr "eitherCustomEx2: "
print eitherCustomEx2 -- Left AnError
putStr "eitherCustomEx3: "
print eitherCustomEx3 -- Left AnotherError
putStr "eitherCustomEx4: "
print eitherCustomEx4 -- Left (AnErrorWithContext "Tag, you're it!")
putStr "eitherCustomEx5: "
print eitherCustomEx5 -- Right 42
putStr "eitherCustomEx6: "
print eitherCustomEx6 -- Left AnError
putStr "eitherCustomEx7: "
print eitherCustomEx7 -- Left (AnErrorWithContext "Tag, you're it!")
------------------------------------------------------------------------------
putStrLn "\nSome examples moving between Either and Maybe:"
putStr "eitherToMaybeEx1: "
print eitherToMaybeEx1 -- Just 42
putStr "eitherToMaybeEx2: "
print eitherToMaybeEx2 -- Nothing
putStr "maybeToEitherEx1: "
print maybeToEitherEx1 -- Right 42
putStr "maybeToEitherEx2: "
print maybeToEitherEx2 -- Left (AnErrorWithContext "I couldn't find a number in this Maybe!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment