{-# OPTIONS --type-in-type #-}

open import NeilPrelude

module Monad (M : Set → Set)

       (_>>='_ : {A B : Set} → M A → (A → M B) → M B)
       (ret    : {A : Set} → A → M A)

where

infixl 4 _>>=_ _>>_

_>>=_ : {A B : Set} → M A → (A → M B) → M B
_>>=_ = _>>='_

_=<<_ : {A B : Set} → (A → M B) → M A → M B
_=<<_ = flip _>>=_

return : {A : Set} → A → M A
return = ret

ap : {A B : Set} → M (A → B) → M A → M B
ap mf ma = mf >>= λ f → ma >>= return ∘ f

liftM : {A B : Set} → (A → B) → M A → M B
liftM f = ap (return f)

join : {A : Set} → M (M A) → M A
join ma = ma >>= id

_>>_ : {A B : Set} → M A → M B → M B
ma >> mb = ma >>= const mb

_<<_ : {A B : Set} → M B → M A → M B
_<<_ = flip _>>_

import Applicative
open Applicative M liftM return ap public

------------------------------------------------------

-- Monads are Higher Order Arrows

-- Arr : Set → Set → Set
-- Arr A B = A → M B

-- marr : {A B : Set} → (A → B) → A → M B
-- marr f a = return (f a)

-- mseq : {A B C : Set} → (A → M B) → (B → M C) → A → M C
-- mseq f g a = f a >>= g

-- mfirst : {A B C : Set} → (A → M B) → A × C → M (B × C)
-- mfirst f (a , c) = liftM (λ b → b , c) (f a)

-- mapply : {A B : Set} → (A → M B) × A → M B
-- mapply = ×-apply

-- import HigherOrderArrow
-- open HigherOrderArrow Arr marr mseq mfirst mapply