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/f9c09a8cfaca4f3e6bf37e2cf2f7aa6a to your computer and use it in GitHub Desktop.
Save hyperrealgopher/f9c09a8cfaca4f3e6bf37e2cf2f7aa6a to your computer and use it in GitHub Desktop.
system-f/fp-course: Monad.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RebindableSyntax #-}
module Course.Monad where
import Course.Applicative
import Course.Core
import Course.ExactlyOne
import Course.Functor
import Course.List
import Course.Optional
import qualified Prelude as P((=<<))
-- | All instances of the `Monad` type-class must satisfy one law. This law
-- is not checked by the compiler. This law is given as:
--
-- * The law of associativity
-- `∀f g x. g =<< (f =<< x) ≅ ((g =<<) . f) =<< x`
class Applicative k => Monad k where
-- Pronounced, bind.
(=<<) ::
(a -> k b)
-> k a
-> k b
infixr 1 =<<
-- | Binds a function on the ExactlyOne monad.
--
-- >>> (\x -> ExactlyOne(x+1)) =<< ExactlyOne 2
-- ExactlyOne 3
instance Monad ExactlyOne where
(=<<) ::
(a -> ExactlyOne b)
-> ExactlyOne a
-> ExactlyOne b
f =<< (ExactlyOne a) = f a
-- | Binds a function on a List.
--
-- >>> (\n -> n :. n :. Nil) =<< (1 :. 2 :. 3 :. Nil)
-- [1,1,2,2,3,3]
instance Monad List where
(=<<) ::
(a -> List b)
-> List a
-> List b
f =<< ls = flatMap f ls
-- | Binds a function on an Optional.
--
-- >>> (\n -> Full (n + n)) =<< Full 7
-- Full 14
instance Monad Optional where
(=<<) ::
(a -> Optional b)
-> Optional a
-> Optional b
f =<< (Full a) = f a
_ =<< Empty = Empty
-- | Binds a function on the reader ((->) t).
--
-- >>> ((*) =<< (+10)) 7
-- 119
instance Monad ((->) t) where
(=<<) ::
(a -> ((->) t b)) -- (t -> b)
-> ((->) t a) -- (t -> a)
-> ((->) t b) -- (t -> b)
(=<<) f1 f2 = \x -> f1 (f2 x) x
-- | Witness that all things with (=<<) and (<$>) also have (<*>).
--
-- >>> ExactlyOne (+10) <**> ExactlyOne 8
-- ExactlyOne 18
--
-- >>> (+1) :. (*2) :. Nil <**> 1 :. 2 :. 3 :. Nil
-- [2,3,4,2,4,6]
--
-- >>> Full (+8) <**> Full 7
-- Full 15
--
-- >>> Empty <**> Full 7
-- Empty
--
-- >>> Full (+8) <**> Empty
-- Empty
--
-- >>> ((+) <**> (+10)) 3
-- 16
--
-- >>> ((+) <**> (+5)) 3
-- 11
--
-- >>> ((+) <**> (+5)) 1
-- 7
--
-- >>> ((*) <**> (+10)) 3
-- 39
--
-- >>> ((*) <**> (+2)) 3
-- 15
(<**>) ::
Monad k =>
k (a -> b)
-> k a
-> k b
(<**>) f1 f2 = f1 <*> f2
infixl 4 <**>
-- | Flattens a combined structure to a single structure.
--
-- >>> join ((1 :. 2 :. 3 :. Nil) :. (1 :. 2 :. Nil) :. Nil)
-- [1,2,3,1,2]
--
-- >>> join (Full Empty)
-- Empty
--
-- >>> join (Full (Full 7))
-- Full 7
--
-- >>> join (+) 7
-- 14
join ::
Monad k =>
k (k a)
-> k a
join structure = id =<< structure
-- | Implement a flipped version of @(=<<)@, however, use only
-- @join@ and @(<$>)@.
-- Pronounced, bind flipped.
--
-- >>> ((+10) >>= (*)) 7
-- 119
(>>=) ::
Monad k =>
k a
-> (a -> k b)
-> k b
(>>=) monad f = join $ f <$> monad
infixl 1 >>=
-- | Implement composition within the @Monad@ environment.
-- Pronounced, Kleisli composition.
--
-- >>> ((\n -> n :. n :. Nil) <=< (\n -> n+1 :. n+2 :. Nil)) 1
-- [2,2,3,3]
(<=<) ::
Monad k =>
(b -> k c) -- mf1
-> (a -> k b) -- mf2
-> a
-> k c
(<=<) mf1 mf2 a = mf2 a >>= mf1
infixr 1 <=<
-----------------------
-- SUPPORT LIBRARIES --
-----------------------
instance Monad IO where
(=<<) =
(P.=<<)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment