packages feed

duoids-0.1.0.0: src/Control/Duoidal.hs

{-# LANGUAGE Safe #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fplugin-opt=NoRecursion:ignore-methods:many,some #-}

-- |
-- Copyright: 2024 Greg Pfeil
-- License: AGPL-3.0-only WITH Universal-FOSS-exception-1.0 OR LicenseRef-commercial
--
-- Provides duoidal operations on functors. This lets us easily mix and match
-- "parallel" and "sequential" operations on structures that have multiple
-- viable `Applicative` instances, like `Either`
-- (`Data.Either.Validation.Validation`) and `System.IO.IO`
-- (`Control.Concurrent.Async.Concurrently`).
--
-- So, for example, when using this, you should ignore the existence of
-- `Data.Either.Validation.Validation`, and always work in `Either` (and
-- `ExceptT`), then, using these operators instead of the usual `Applicative`
-- and `Monad` operators, you will have behavior that correctly mixes the
-- accumulation of errors with the monadic "first failure" semantics. This
-- should lawfully always do what you want, without running into the
-- “`Applicative` semantics must match `Monad` semantics” problem.
--
-- - ♢ – `Parallel`
-- - ★ – `Sequential`
--
-- Because of the historical duplication of `Applicative` (`pure`, `<*`,
-- `sequenceA`) and `Monad` (`return`, `<<`, `sequence`) operations, we don’t
-- need to come up with new names for everything. We just make the definitions
-- actually distinct, whereas they previously had to align.
--
-- __NB__: It’s easy to use the wrong operations when this module is imported,
--         because they have the same names as ones that already exist in
--         @base@. Here are some recommendations on how to use it successfully.
--      1. If you import everything qualified anyway and use `NoDefaultPrelude`,
--         great – qualify this module, and use it alongside qualified
--         `Applicative` and `Monad` operations.
--      2. If you use unqualified imports, I recommend importing this module
--         unqualified /with no import list/. You will likely get errors like
--         either “ambigious occurrence of …” or “couldn’t satisfy constraint
--         Duoidal …”. When the former happens, you can either hide or qualify
--         the import of the non-duoidal operation. When the latter happens, add
--         a qualified import of the non-duoidal operation. The reason for
--         preferring the operations from this module is because they are more
--         restricted and, when they apply, they are likely to do the right
--         thing. If the other operations are preferred, your likely to find
--         yourself with monadic semantics when you were hoping to get duoidal
--         semantics.
--
-- This module supports various combinations of @do@-notation for `Duoid`
-- instances.
--
-- The ideal use case for this module is in combination with @ApplicativeDo@ and
-- either @QualifiedDo@ (for case-by-case @do@ semantics) or @RebindableSyntax@
-- (for module-wide @do@ semantics).
--
--  ## Example
--
--   When the independent steps succeed, we just return the dependent result,
--   like a `Monad`.
--
-- >>> :{
-- Duoidal.do
--   x <- Right "x"
--   y <- Right "y"
--   Left ["Couldn't merge records " <> x <> " and " <> y]
-- :}
-- Left ["Couldn't merge records x and y"]
--
--   But when the independent steps fail, we collect all the independent
--   failures, like `Validation`.
--
-- >>> :{
-- Duoidal.do
--   x <- Left ["Couldn't find record x"]
--   y <- Left ["Couldn't find record y"]
--   Left ["Couldn't merge records " <> x <> " and " <> y]
-- :}
-- Left ["Couldn't find record x","Couldn't find record y"]
--
--  __NB__: `fail` isn’t part of the `Duoidal` interface, so this just
--          re-exports the one from `MonadFail`.
--
-- ## resources
--
-- - https://ncatlab.org/nlab/show/duoidal+category
-- - https://blogs.ncl.ac.uk/andreymokhov/united-monoids/
module Control.Duoidal
  ( Duoidal,
    Normal,
    DuoidalIO (liftIO),

    -- * parallel `Applicative` operations
    Parallel (Parallel, getParallel),
    (<*>),
    (<**>),
    (*>),
    (<*),
    bisequenceA,
    bitraverse,
    liftA2,
    liftA3,
    pure,
    traverse,
    traverse_,
    for,
    for_,
    sequenceA,
    sequenceA_,
    forever,
    unless,
    when,

    -- * sequential `Monad` operations
    Sequential (Sequential, getSequential),
    (>>),
    (>>=),
    (>=>),
    (=<<),
    (<=<),
    join,
    ap,
    (<<>>),
    return,
    (<<),
    bisequence,
    bimapM,
    liftM2,
    liftM3,
    mapM,
    mapM_,
    forM,
    forM_,
    sequence,
    sequence_,
    forever',
    unless',
    when',

    -- * re-exported operations for @QualifiedDo@
    fmap,

    -- * instance helpers
    normalPure,
    sequentialAp,
    sequentialBind,
    sequentialLiftA2,
    sequentialPure,

    -- * duoids from commutative `Monad`s
    Commutative (Commutative, getCommutative),
    commutativeAp,
  )
where

import "base" Control.Applicative
  ( Alternative,
    Applicative,
    Const (Const),
    empty,
    (<|>),
  )
import "base" Control.Applicative qualified as Base
  ( liftA2,
    liftA3,
    pure,
    (*>),
    (<*),
    (<**>),
    (<*>),
  )
import "base" Control.Category ((.))
import "base" Control.Monad (Monad)
import "base" Control.Monad qualified as Base
  ( ap,
    forever,
    join,
    liftM2,
    return,
    unless,
    when,
    (<=<),
    (=<<),
    (>=>),
    (>>=),
  )
import "base" Data.Bifunctor (bimap, first)
import "base" Data.Bitraversable (Bitraversable)
import "base" Data.Bitraversable qualified as Base
  ( bimapM,
    bisequence,
    bitraverse,
  )
import "base" Data.Bool (Bool)
import "base" Data.Complex (Complex)
import "base" Data.Either (Either (Left, Right), either)
import "base" Data.Eq (Eq)
import "base" Data.Foldable (Foldable)
import "base" Data.Foldable qualified as Base (for_, traverse_)
import "base" Data.Function (const, ($))
import "base" Data.Functor (Functor, fmap)
import "base" Data.Functor.Identity (Identity)
import "base" Data.Kind (Constraint, Type)
import "base" Data.Maybe (Maybe)
import "base" Data.Monoid (Dual, Monoid, Sum, mempty)
import "base" Data.Monoid qualified as Monoid
import "base" Data.Ord (Down, Ord)
import "base" Data.Proxy (Proxy)
import "base" Data.Semigroup (Max, Min, Semigroup, (<>))
import "base" Data.Semigroup qualified as Semigroup
import "base" Data.Traversable (Traversable)
import "base" Data.Traversable qualified as Base (for, traverse)
import "base" Data.Tuple (Solo)
import "base" GHC.TypeError (ErrorMessage (Text), TypeError)
import "base" System.IO (IO)
import "base" Text.Read (Read)
import "base" Text.Show (Show)
import "this" Control.Duoidal.Async qualified as Async
import "this" Data.Duoid (Duoid, pempty, sempty, (>->), (|-|))
import "this" Data.Duoid qualified as Duoid (Normal)
import "base" Prelude (error)

-- $setup
-- >>> :seti -XApplicativeDo
-- >>> :seti -XQualifiedDo
-- >>> import "duoids" Control.Duoidal qualified as Duoidal

type Parallel :: forall {k}. (k -> Type) -> k -> Type
newtype Parallel f a = Parallel {getParallel :: f a}
  deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)

-- TODO: Replace this with `Unsatisfiable` and `unsatisfiable` once we no longer
--       support GHC 9.6. (And remove the `Applicative` constraint.)
instance
  ( TypeError
      ('Text "Can’t have a `Monad` instance over the `Parallel` newtype."),
    Applicative (Parallel f)
  ) =>
  Monad (Parallel f)
  where
  (>>=) = error "unreachable"

type Sequential :: forall {k}. (k -> Type) -> k -> Type
newtype Sequential f a = Sequential {getSequential :: f a}
  deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)

-- | A `Duoidal` functor is a `Monad`, but is also an `Applicative` functor in
--   two ways. One is the `Applicative` induced by the `Monad`, but the other is
--   a `Parallel` `Applicative`, which relates to the monadic (“sequential”)
--   `Applicative` via the duoid laws.
--
--  __NB__: Instances are automatically coalesced from the `Sequential` `Monad`
--          and `Parallel` `Applicative` instances.
type Duoidal :: (Type -> Type) -> Constraint
class (Functor f, Applicative (Parallel f), Monad (Sequential f)) => Duoidal f

-- interchange :: f a -> f a
-- splitUnit :: f a -> f (a, a)
-- joinUnit :: f (a, a) -> f a
-- swapUnit :: f a -> f a
-- swapUnit = defaultSwapUnit

instance
  (Functor f, Applicative (Parallel f), Monad (Sequential f)) =>
  Duoidal f

-- I→≅(J⋆I)⋄(I⋆J)→(J⋄I)⋆(I⋄J)→≅J

-- defaultSwapUnit :: (Duoidal f) => f a -> f a
-- defaultSwapUnit =
--   to rightIdentity --                                           J
--     . bimap (to rightIdentity) (to leftIdentity) --           J ★ J
--     . interchange --                                    (J ♢ I) ★ (I ♢ J)
--     . bimap (from leftIdentity) (from rightIdentity) -- (J ★ I) ♢ (I ★ J)
--     . from leftIdentity --                                    I ♢ I

-- | `Normal` `Duoidal` functors are ones where the two identities (`pure` and
--   `return`) are isomorphic.
type Normal :: (Type -> Type) -> Constraint
class (Duoidal f) => Normal f

--   where
--   -- | This must form an isomorphism with `swapUnit`.
--   unswapUnit :: f a -> f a

-- | The `Parallel` `Applicative` version of `Base.pure`.
pure :: (Duoidal f) => a -> f a
pure = getParallel . Base.pure

return :: (Duoidal f) => a -> f a
return = getSequential . Base.pure

-- | The `Parallel` `Applicative` version of `Base.liftA2`.
liftA2 :: (Duoidal f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a = getParallel . Base.liftA2 f (Parallel a) . Parallel

liftM2 :: (Duoidal f) => (a -> b -> c) -> f a -> f b -> f c
liftM2 f a = getSequential . Base.liftA2 f (Sequential a) . Sequential

-- | The `Parallel` `Applicative` version of `Base.liftA3`.
liftA3 :: (Duoidal f) => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 f a b = getParallel . Base.liftA3 f (Parallel a) (Parallel b) . Parallel

liftM3 :: (Duoidal f) => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftM3 f a b =
  getSequential . Base.liftA3 f (Sequential a) (Sequential b) . Sequential

-- | The `Parallel` `Applicative` version of `Base.<*>`.
(<*>) :: (Duoidal f) => f (a -> b) -> f a -> f b
f <*> g = getParallel $ Parallel f Base.<*> Parallel g

infixl 4 <*>

ap :: (Duoidal f) => f (a -> b) -> f a -> f b
ap f g = getSequential $ Sequential f Base.<*> Sequential g

-- | The `Parallel` `Applicative` version of `Base.<**>`.
(<**>) :: (Duoidal f) => f a -> f (a -> b) -> f b
f <**> g = getParallel $ Parallel f Base.<**> Parallel g

infixl 4 <**>

-- |
--
--  __NB__: This is missing from "Control.Monad", but added here to maintain the
--         pairwise definitions of `Parallel` and `Sequential` `Applicative`
--         operations.
(<<>>) :: (Duoidal f) => f a -> f (a -> b) -> f b
f <<>> g = getSequential $ Sequential f Base.<**> Sequential g

infixl 1 <<>>

-- | The `Parallel` `Applicative` version of `Base.*>`.
(*>) :: (Duoidal f) => f a -> f b -> f b
a *> b = getParallel $ Parallel a Base.*> Parallel b

infixl 4 *>

(>>) :: (Duoidal f) => f a -> f b -> f b
a >> b = getSequential (Sequential a Base.*> Sequential b)

infixl 1 >>

-- | The `Parallel` `Applicative` version of `Base.<*`.
(<*) :: (Duoidal f) => f a -> f b -> f a
a <* b = getParallel $ Parallel a Base.<* Parallel b

infixl 4 <*

-- |
--
--  __NB__: This is missing from "Control.Monad", but added here to maintain the
--         pairwise definitions of `Parallel` and `Sequential` `Applicative`
--         operations.
(<<) :: (Monad f) => f a -> f b -> f a
(<<) = (Base.<*)

infixl 1 <<

-- | The `Parallel` `Applicative` version of `Base.bisequenceA`.
bisequenceA :: (Bitraversable t, Duoidal f) => t (f a) (f b) -> f (t a b)
bisequenceA = getParallel . Base.bisequence . bimap Parallel Parallel

-- |
--
--  __NB__: In "Data.Bitraversable", this is identical to `Base.bisequenceA`
--          (it’s not overconstrained to `Monad`), but here we give it
--          sequential semantics, to parallel the difference between `sequenceA`
--          and `sequence`.
bisequence :: (Bitraversable t, Duoidal f) => t (f a) (f b) -> f (t a b)
bisequence = getSequential . Base.bisequence . bimap Sequential Sequential

-- | The `Parallel` `Applicative` version of `Base.bitraverse`.
bitraverse ::
  (Bitraversable t, Duoidal f) => (a -> f c) -> (b -> f d) -> t a b -> f (t c d)
bitraverse f g = getParallel . Base.bitraverse (Parallel . f) (Parallel . g)

-- |
--
--  __NB__: In "Data.Bitraversable", this is identical to `Base.bitraverse`
--          (it’s not overconstrained to `Monad`), but here we give it
--          sequential semantics, to parallel the difference between `sequenceA`
--          and `sequence`.
bimapM ::
  (Bitraversable t, Duoidal f) => (a -> f c) -> (b -> f d) -> t a b -> f (t c d)
bimapM f g = getSequential . Base.bimapM (Sequential . f) (Sequential . g)

-- | The `Parallel` `Applicative` version of `Base.traverse`.
traverse :: (Traversable t, Duoidal f) => (a -> f b) -> t a -> f (t b)
traverse f = getParallel . Base.traverse (Parallel . f)

mapM :: (Traversable t, Duoidal f) => (a -> f b) -> t a -> f (t b)
mapM f = getSequential . Base.traverse (Sequential . f)

traverse_ :: (Foldable t, Duoidal f) => (a -> f b) -> t a -> f ()
traverse_ f = getParallel . Base.traverse_ (Parallel . f)

mapM_ :: (Foldable t, Duoidal f) => (a -> f b) -> t a -> f ()
mapM_ f = getSequential . Base.traverse_ (Sequential . f)

for :: (Traversable t, Duoidal f) => t a -> (a -> f b) -> f (t b)
for a f = getParallel . Base.for a $ Parallel . f

forM :: (Traversable t, Duoidal f) => t a -> (a -> f b) -> f (t b)
forM a f = getSequential . Base.for a $ Sequential . f

for_ :: (Foldable t, Duoidal f) => t a -> (a -> f b) -> f ()
for_ a f = getParallel . Base.for_ a $ Parallel . f

forM_ :: (Foldable t, Duoidal f) => t a -> (a -> f b) -> f ()
forM_ a f = getSequential . Base.for_ a $ Sequential . f

-- | The `Parallel` `Applicative` version of `Base.sequenceA`.
sequenceA :: (Traversable t, Duoidal f) => t (f a) -> f (t a)
sequenceA = getParallel . Base.traverse Parallel

sequence :: (Traversable t, Duoidal f) => t (f a) -> f (t a)
sequence = getSequential . Base.traverse Sequential

-- | The `Parallel` `Applicative` version of `Base.sequenceA`.
sequenceA_ :: (Foldable t, Duoidal f) => t (f a) -> f ()
sequenceA_ = getParallel . Base.traverse_ Parallel

sequence_ :: (Foldable t, Duoidal f) => t (f a) -> f ()
sequence_ = getSequential . Base.traverse_ Sequential

forever :: (Duoidal f) => f a -> f b
forever = getParallel . Base.forever . Parallel

forever' :: (Duoidal f) => f a -> f b
forever' = getSequential . Base.forever . Sequential

-- | The `Parallel` `Applicative` version of `Base.unless`.
unless :: (Duoidal f) => Bool -> f () -> f ()
unless b = getParallel . Base.unless b . Parallel

unless' :: (Duoidal f) => Bool -> f () -> f ()
unless' b = getSequential . Base.unless b . Sequential

-- | The `Parallel` `Applicative` version of `Base.when`.
when :: (Duoidal f) => Bool -> f () -> f ()
when b = getParallel . Base.when b . Parallel

when' :: (Duoidal f) => Bool -> f () -> f ()
when' b = getSequential . Base.when b . Sequential

join :: (Duoidal f) => f (f a) -> f a
join = getSequential . Base.join . Sequential . fmap Sequential

(>>=) :: (Duoidal f) => f a -> (a -> f b) -> f b
a >>= f = getSequential (Sequential a Base.>>= Sequential . f)

infixr 1 >>=

(=<<) :: (Duoidal f) => (a -> f b) -> f a -> f b
f =<< a = getSequential (Sequential . f Base.=<< Sequential a)

infixr 1 =<<

(>=>) :: (Duoidal f) => (a -> f b) -> (b -> f c) -> a -> f c
f >=> g = getSequential . (Sequential . f Base.>=> Sequential . g)

infixr 1 >=>

(<=<) :: (Duoidal f) => (b -> f c) -> (a -> f b) -> a -> f c
f <=< g = getSequential . (Sequential . f Base.<=< Sequential . g)

infixr 1 <=<

type DuoidalIO :: (Type -> Type) -> Constraint
class (Duoidal m) => DuoidalIO m where
  liftIO :: IO a -> m a

normalPure :: (Applicative f) => a -> Parallel f a
normalPure = Parallel . Base.pure

sequentialPure :: (Monad f) => a -> Sequential f a
sequentialPure = Sequential . Base.return

sequentialLiftA2 ::
  (Monad f) =>
  (a -> b -> c) ->
  Sequential f a ->
  Sequential f b ->
  Sequential f c
sequentialLiftA2 f (Sequential a) = Sequential . Base.liftA2 f a . getSequential
{-# DEPRECATED sequentialLiftA2 "use ‘sequentialAp’ instead" #-}

sequentialAp ::
  (Monad f) => Sequential f (a -> b) -> Sequential f a -> Sequential f b
sequentialAp (Sequential f) = Sequential . Base.ap f . getSequential

sequentialBind ::
  (Monad f) => Sequential f a -> (a -> Sequential f b) -> Sequential f b
sequentialBind (Sequential a) f = Sequential $ a Base.>>= (getSequential . f)

-- INSTANCES

-- | Commutative `Monad`s form a duoid with themselves.
--
--   You can use this with @DerivingVia@ to create instances for your own
--   commutative `Monad`s.
--
--   For existing types, instances for types in base should be available here,
--   but those for other packages may not exist. For those that don’t, you can
--
-- 1. wrap the type in `Commutative` when you need the instance,
-- 2. define orphan instances that look like the instances for `Commutative`, or
-- 3. use the provided operations (like `commutativeAp`) directly.
--
--   Some examples of commutative monads:
-- - those isomorphic to `Identity` (many newtypes fall into this bucket)
-- - reader (@->@)
-- - `Maybe`
-- - `Proxy`
--
--   You can also wrap types that already have `Duoidal` instances in
--   `Commutative` as well (as long as they have an unwrapped `Monad` instance).
--   If their existing `Duoidal` instance isn’t the commutative one, the
--   `Commutative` wrapper will give you the commutative one. I don’t know if
--   this one is correct, but if you have a commutative writer (say, @`Writer`
--   (`Set` `Char`)@), wrapping it in `Commutative` would give you a `Duoidal`
--   instance that behaves commutatively. However, @`Set` a@ should already have
--   a commutative `Duoid` instance, so I don’t think it actually buys you
--   anything.
--
--  __NB__: Don’t use this newtype to turn a non-commutative `Monad` into a
--          duoid.
type Commutative :: forall {k}. (k -> Type) -> k -> Type
newtype Commutative f a = Commutative {getCommutative :: f a}
  deriving stock (Eq, Ord, Read, Show, Functor, Foldable, Traversable)

instance (Monad f) => Applicative (Commutative f) where
  pure = Commutative . Base.return
  Commutative f <*> Commutative a = Commutative $ Base.ap f a

instance (Monad f) => Monad (Commutative f) where
  Commutative a >>= f = Commutative $ a Base.>>= (getCommutative . f)

commutativeAp ::
  (Monad f) =>
  Parallel f (a -> b) ->
  Parallel f a ->
  Parallel f b
commutativeAp (Parallel f) = Parallel . Base.ap f . getParallel

instance (Monad f) => Applicative (Parallel (Commutative f)) where
  pure = normalPure
  (<*>) = commutativeAp

instance (Monad f) => Applicative (Sequential (Commutative f)) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance (Monad f) => Monad (Sequential (Commutative f)) where
  (>>=) = sequentialBind

instance (Monad f) => Normal (Commutative f)

-- `Complex` is a commutative duoidal functor

instance Applicative (Parallel Complex) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Complex) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Complex) where
  (>>=) = sequentialBind

instance Normal Complex

-- `Down` is a commutative duoidal functor

instance Applicative (Parallel Down) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Down) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Down) where
  (>>=) = sequentialBind

instance Normal Down

-- `Dual` is a commutative duoidal functor

instance Applicative (Parallel Dual) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Dual) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Dual) where
  (>>=) = sequentialBind

instance Normal Dual

-- `Monoid.First` is a commutative duoidal functor

instance Applicative (Parallel Monoid.First) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Monoid.First) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Monoid.First) where
  (>>=) = sequentialBind

instance Normal Monoid.First

-- `Semigroup.First` is a commutative duoidal functor

instance Applicative (Parallel Semigroup.First) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Semigroup.First) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Semigroup.First) where
  (>>=) = sequentialBind

instance Normal Semigroup.First

-- `Identity` is a commutative duoidal functor

instance Applicative (Parallel Identity) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Identity) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Identity) where
  (>>=) = sequentialBind

instance Normal Identity

-- `Monoid.Last` is a commutative duoidal functor

instance Applicative (Parallel Monoid.Last) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Monoid.Last) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Monoid.Last) where
  (>>=) = sequentialBind

instance Normal Monoid.Last

-- `Semigroup.Last` is a commutative duoidal functor

instance Applicative (Parallel Semigroup.Last) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Semigroup.Last) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Semigroup.Last) where
  (>>=) = sequentialBind

instance Normal Semigroup.Last

-- `Max` is a commutative duoidal functor

instance Applicative (Parallel Max) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Max) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Max) where
  (>>=) = sequentialBind

instance Normal Max

-- `Maybe` is a commutative duoidal functor

instance Applicative (Parallel Maybe) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Maybe) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Maybe) where
  (>>=) = sequentialBind

instance Normal Maybe

-- `Min` is a commutative duoidal functor

instance Applicative (Parallel Min) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Min) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Min) where
  (>>=) = sequentialBind

instance Normal Min

-- `Monoid.Product` is a commutative duoidal functor

instance Applicative (Parallel Monoid.Product) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Monoid.Product) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Monoid.Product) where
  (>>=) = sequentialBind

instance Normal Monoid.Product

-- `Proxy` is a commutative duoidal functor

instance Applicative (Parallel Proxy) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Proxy) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Proxy) where
  (>>=) = sequentialBind

instance Normal Proxy

-- `Solo` is a commutative duoidal functor

instance Applicative (Parallel Solo) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Solo) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Solo) where
  (>>=) = sequentialBind

instance Normal Solo

-- `Sum` is a commutative duoidal functor

instance Applicative (Parallel Sum) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential Sum) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential Sum) where
  (>>=) = sequentialBind

instance Normal Sum

-- reader is a commutative duoidal functor

instance Applicative (Parallel ((->) r)) where
  pure = normalPure
  (<*>) = commutativeAp

instance Applicative (Sequential ((->) r)) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential ((->) r)) where
  (>>=) = sequentialBind

instance Normal ((->) r)

-- Const

instance (Monoid a) => Applicative (Parallel (Const a)) where
  pure = normalPure
  liftA2 f (Parallel a) (Parallel b) = Parallel $ liftA2 f a b

instance (Monoid a) => Applicative (Sequential (Const a)) where
  pure = Sequential . pure
  liftA2 f (Sequential a) = Sequential . Base.liftA2 f a . getSequential

-- | The `Const` duoidal functor provides an illustration of why we need to have
--   both `Parallel` and `Sequential` newtypes – relying on the underlying
--   `Applicative` (and only having the `Sequential` newtype) would mean that
--   any duoidal structure would only have a `Monad` available under
--   `Sequential`, which would be a prettty serious impact. On the other hand,
--   relying on the underlying `Monad` (and only having the `Parallel` newtype)
--   is much more natural, but `Const`, for example, having a `Monad` instance
--   would make it basically useless, and the more interesting `Applicative`
--   instance would only be available under the `Parallel` netwype. The current
--   structure allows either the `Applicative` or `Monad` instance to be the one
--   exposed directly.
instance (Monoid a) => Monad (Sequential (Const a)) where
  Sequential (Const a) >>= _ = Sequential $ Const a

-- Either

instance (Semigroup e) => Applicative (Parallel (Either e)) where
  pure = normalPure

  liftA2 f (Parallel a) (Parallel b) =
    Parallel $ case (a, b) of
      (Left e, Left e') -> Left $ e <> e'
      (Left e, Right _) -> Left e
      (Right _, Left e') -> Left e'
      (Right x, Right y) -> Right $ f x y

instance (Semigroup e) => Applicative (Sequential (Either e)) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance (Semigroup e) => Monad (Sequential (Either e)) where
  (>>=) = sequentialBind

instance (Semigroup e) => Normal (Either e)

-- | `Parallel` `Alternative` for `Either` collects failures unless there is a
--   success.
--
--  __TODO__: The unwrapped version of this should be added to @base@.
instance (Monoid e) => Alternative (Parallel (Either e)) where
  empty = Parallel $ Left mempty
  Parallel a <|> Parallel b = Parallel $ either (\e -> first (e <>) b) pure a

-- | `Sequential` `Alternative` for `Either` returns the last failure unless
--   there is a success.
instance (Monoid e) => Alternative (Sequential (Either e)) where
  empty = Sequential $ Left mempty
  Sequential a <|> Sequential b = Sequential $ either (const b) pure a

-- IO

instance Applicative (Parallel IO) where
  pure = normalPure
  liftA2 f (Parallel left) (Parallel right) =
    Parallel $ Async.liftA2 f left right

instance Applicative (Sequential IO) where
  pure = sequentialPure
  (<*>) = sequentialAp

instance Monad (Sequential IO) where
  (>>=) = sequentialBind

instance Normal IO

-- | `Parallel` `Alternative` for `IO` returns the first action that completes,
--   canceling others. `empty` waits indefinitely.
instance Alternative (Parallel IO) where
  empty = Parallel Async.empty
  Parallel a <|> Parallel b = Parallel $ a Async.<|> b

instance Alternative (Sequential IO) where
  empty = Sequential empty
  Sequential a <|> Sequential b = Sequential $ a <|> b

-- tuples

-- |
--
--  __TODO__: For this one, we really _do_ need the separate `Sequential`,
--            because the `Monad` on @((,) a)@ only has a `Monoid` constraint,
--            and I don’t think we want to bless one of the `Duoid` monoids as
--            we currently do with the `Duoidal` functors.
instance (Duoid a) => Applicative (Parallel ((,) a)) where
  pure = Parallel . (pempty,)
  liftA2 f (Parallel (a, x)) (Parallel (a', y)) = Parallel (a |-| a', f x y)

instance (Duoid a) => Applicative (Sequential ((,) a)) where
  pure = Sequential . (sempty,)
  liftA2 = Base.liftM2

-- |
--
--  __NB__: This instance is different from the @`Monad` ((,) a)@ instance,
--          because it requires a `Duoid` with a /potentially/ distinct parallel
--          operation.
instance (Duoid a) => Monad (Sequential ((,) a)) where
  Sequential (u, a) >>= k =
    case k a of Sequential (v, b) -> Sequential (u >-> v, b)

-- | A writer is a `Normal` `Duoidal` functor when the writee is a
--   `Duoid.Normal` `Duoid`.
instance (Duoid.Normal a) => Normal ((,) a)

instance (Duoid a, Duoid b) => Applicative (Parallel ((,,) a b)) where
  pure = Parallel . (pempty,pempty,)
  liftA2 f (Parallel (a, b, x)) (Parallel (a', b', y)) =
    Parallel (a |-| a', b |-| b', f x y)

instance (Duoid a, Duoid b) => Applicative (Sequential ((,,) a b)) where
  pure = Sequential . (sempty,sempty,)
  liftA2 = Base.liftM2

instance (Duoid a, Duoid b) => Monad (Sequential ((,,) a b)) where
  Sequential (u, v, a) >>= k =
    case k a of Sequential (u', v', b) -> Sequential (u >-> u', v >-> v', b)

-- | A writer is a `Normal` `Duoidal` functor when the writee is a
--   `Duoid.Normal` `Duoid`.
instance (Duoid.Normal a, Duoid.Normal b) => Normal ((,,) a b)

instance
  (Duoid a, Duoid b, Duoid c) =>
  Applicative (Parallel ((,,,) a b c))
  where
  pure = Parallel . (pempty,pempty,pempty,)
  liftA2 f (Parallel (a, b, c, x)) (Parallel (a', b', c', y)) =
    Parallel (a |-| a', b |-| b', c |-| c', f x y)

instance
  (Duoid a, Duoid b, Duoid c) =>
  Applicative (Sequential ((,,,) a b c))
  where
  pure = Sequential . (sempty,sempty,sempty,)
  liftA2 = Base.liftM2

instance (Duoid a, Duoid b, Duoid c) => Monad (Sequential ((,,,) a b c)) where
  Sequential (u, v, w, a) >>= k =
    case k a of
      Sequential (u', v', w', b) -> Sequential (u >-> u', v >-> v', w >-> w', b)

-- | A writer is a `Normal` `Duoidal` functor when the writee is a
--   `Duoid.Normal` `Duoid`.
instance
  (Duoid.Normal a, Duoid.Normal b, Duoid.Normal c) =>
  Normal ((,,,) a b c)