Skip to content

Instantly share code, notes, and snippets.

@hyperrealgopher
Created March 27, 2021 07:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyperrealgopher/fbaa716cb3d34be3e5bd882aba0ea603 to your computer and use it in GitHub Desktop.
Save hyperrealgopher/fbaa716cb3d34be3e5bd882aba0ea603 to your computer and use it in GitHub Desktop.
system-f/fp-course: Optional.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Course.Optional where
import qualified Control.Applicative as A
import qualified Control.Monad as M
import Course.Core
import qualified Prelude as P
-- | The `Optional` data type contains 0 or 1 value.
--
-- It might be thought of as a list, with a maximum length of one.
data Optional a =
Full a
| Empty
deriving (Eq, Show)
-- | Map the given function on the possible value.
--
-- >>> mapOptional (+1) Empty
-- Empty
--
-- >>> mapOptional (+1) (Full 8)
-- Full 9
mapOptional ::
(a -> b)
-> Optional a
-> Optional b
mapOptional f (Full a) = Full (f a)
mapOptional _ Empty = Empty
-- | Bind the given function on the possible value.
--
-- >>> bindOptional Full Empty
-- Empty
--
-- >>> bindOptional (\n -> if even n then Full (n - 1) else Full (n + 1)) (Full 8)
-- Full 7
--
-- >>> bindOptional (\n -> if even n then Full (n - 1) else Full (n + 1)) (Full 9)
-- Full 10
bindOptional ::
(a -> Optional b)
-> Optional a
-> Optional b
bindOptional f (Full a) = f a
bindOptional _ Empty = Empty
-- | Return the possible value if it exists; otherwise, the second argument.
--
-- >>> Full 8 ?? 99
-- 8
--
-- >>> Empty ?? 99
-- 99
(??) ::
Optional a
-> a
-> a
(Full a) ?? _ = a
Empty ?? x = x
-- | Try the first optional for a value. If it has a value, use it; otherwise,
-- use the second value.
--
-- >>> Full 8 <+> Empty
-- Full 8
--
-- >>> Full 8 <+> Full 9
-- Full 8
--
-- >>> Empty <+> Full 9
-- Full 9
--
-- >>> Empty <+> Empty
-- Empty
(<+>) ::
Optional a
-> Optional a
-> Optional a
Empty <+> x = x
x <+> Empty = x
x <+> _ = x
-- | Replaces the Full and Empty constructors in an optional.
--
-- >>> optional (+1) 0 (Full 8)
-- 9
--
-- >>> optional (+1) 0 Empty
-- 0
optional ::
(a -> b)
-> b
-> Optional a
-> b
optional f _ (Full a) = f a
optional _ d Empty = d
applyOptional :: Optional (a -> b) -> Optional a -> Optional b
applyOptional f a = bindOptional (\f' -> mapOptional f' a) f
twiceOptional :: (a -> b -> c) -> Optional a -> Optional b -> Optional c
twiceOptional f = applyOptional . mapOptional f
contains :: Eq a => a -> Optional a -> Bool
contains _ Empty = False
contains a (Full z) = a == z
instance P.Functor Optional where
fmap =
M.liftM
instance A.Applicative Optional where
(<*>) =
M.ap
pure =
Full
instance P.Monad Optional where
(>>=) =
flip bindOptional
return =
Full
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment