packages feed

cherry-core-alpha-0.4.0.0: src/Maybe.hs

{-|

Module      : Maybe
Description : A `Maybe` can help you with optional arguments, error handling, and records with optional fields.
License     : BSD 3
Maintainer  : terezasokol@gmail.com
Stability   : experimental
Portability : POSIX

A `Maybe` can help you with optional arguments, error handling, and records with optional fields.

-}

module Maybe
  ( -- * Definition
    Maybe(..)

    -- * Common Helpers
  , withDefault, map, map2, map3, map4, map5

    -- * Chaining Maybes
  , andThen

    -- * From Haskell types
  , fromHMaybe
  )
where

import Prelude (Applicative, Char, Eq, Functor, Monad, Num, Ord, Show, flip, fromIntegral, mappend, mconcat, otherwise, pure)
import qualified Prelude
import qualified Internal.Shortcut as Shortcut


{-| Represent values that may or may not exist. It can be useful if you have a
record field that is only filled in sometimes. Or if a function takes a value
sometimes, but does not absolutely need it.

  >  -- A person, but maybe we do not know their age.
  >  data Person = Person
  >      { name :: String
  >      , age :: Maybe Int
  >      }
  >
  >  tom = { name = "Tom", age = Just 42 }
  >  sue = { name = "Sue", age = Nothing }
-}
data Maybe a
  = Just a
  | Nothing
  deriving (Prelude.Show, Prelude.Eq)


instance Functor Maybe where
  fmap func maybe =
    case maybe of
      Just a -> Just (func a)
      Nothing -> Nothing


instance Applicative Maybe where
  pure a =
    Just a

  (<*>) func maybe =
    case (func, maybe) of
      (Just f, Just a) -> Just (f a)
      _ -> Nothing


instance Monad Maybe where
  maybe >>= func =
   case maybe of
      Just a -> func a
      Nothing -> Nothing


{-| Provide a default value, turning an optional value into a normal
value.  This comes in handy when paired with functions like
[`Dict.get`](Dict#get) which gives back a `Maybe`.

  >  withDefault 100 (Just 42)   -- 42
  >  withDefault 100 Nothing     -- 100
  >
  >  withDefault "unknown" (Dict.get "Tom" Dict.empty)   -- "unknown"

Note: This can be overused! Many cases are better handled by a `case`
expression. And if you end up using `withDefault` a lot, it can be a good sign
that a [custom type](https://guide.elm-lang.org/types/custom_types.html) will clean your code up quite a bit!
-}
withDefault :: a -> Maybe a -> a
withDefault value maybe =
  case maybe of
    Just a -> a
    Nothing -> value


{-| Transform a `Maybe` value with a given function:

  >  map sqrt (Just 9) == Just 3
  >  map sqrt Nothing  == Nothing

  >  map sqrt (String.toFloat "9") == Just 3
  >  map sqrt (String.toFloat "x") == Nothing

-}
map :: (a -> b) -> Maybe a -> Maybe b
map =
  Shortcut.map


{-| Apply a function if all the arguments are `Just` a value.

  >  map2 (+) (Just 3) (Just 4) == Just 7
  >  map2 (+) (Just 3) Nothing == Nothing
  >  map2 (+) Nothing (Just 4) == Nothing
  >
  >  map2 (+) (String.toInt "1") (String.toInt "123") == Just 124
  >  map2 (+) (String.toInt "x") (String.toInt "123") == Nothing
  >  map2 (+) (String.toInt "1") (String.toInt "1.3") == Nothing
-}
map2 :: (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value
map2 =
  Shortcut.map2


{-|-}
map3 :: (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value
map3 =
  Shortcut.map3


{-|-}
map4 :: (a -> b -> c -> d -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value
map4 =
  Shortcut.map4


{-|-}
map5 :: (a -> b -> c -> d -> e -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe e -> Maybe value
map5 =
  Shortcut.map5


{-| Chain together many computations that may fail. It is helpful to see its
definition:

  >  andThen :: (a -> Maybe b) -> Maybe a -> Maybe b
  >  andThen callback maybe =
  >      case maybe of
  >          Just value ->
  >              callback value
  >
  >          Nothing ->
  >              Nothing

This means we only continue with the callback if things are going well. For
example, say you need to parse some user input as a month:

  >  parseMonth :: String -> Maybe Int
  >  parseMonth userInput =
  >      String.toInt userInput
  >        |> andThen toValidMonth
  >
  >  toValidMonth :: Int -> Maybe Int
  >  toValidMonth month =
  >      if 1 <= month && month <= 12 then
  >          Just month
  >      else
  >          Nothing

In the `parseMonth` function, if `String.toInt` produces `Nothing` (because
the `userInput` was not an integer) this entire chain of operations will
short-circuit and result in `Nothing`. If `toValidMonth` results in `Nothing`,
again the chain of computations will result in `Nothing`.
-}
andThen :: (a -> Maybe b) -> Maybe a -> Maybe b
andThen =
  Shortcut.andThen


{-| -}
fromHMaybe :: Prelude.Maybe a -> Maybe a
fromHMaybe maybe =
  case maybe of
    Prelude.Just a -> Just a
    Prelude.Nothing -> Nothing