packages feed

duoidal-transformers-0.0.1.0: src/Control/Duoidal/Trans/Orphans.hs

{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- |
-- Copyright: 2024 Greg Pfeil
-- License: AGPL-3.0-only WITH Universal-FOSS-exception-1.0 OR LicenseRef-commercial
module Control.Duoidal.Trans.Orphans () where

import "base" Control.Applicative (Applicative)
import "base" Control.Applicative qualified as Base (liftA2, pure, (<*>))
import "base" Control.Category ((.))
import "base" Control.Monad (Monad)
import "base" Control.Monad qualified as Base ((>>=))
import "base" Data.Either (Either (Left), either)
import "base" Data.Function (($))
import "base" Data.Functor (fmap)
import "base" Data.Functor.Compose (Compose (Compose), getCompose)
import "base" Data.Semigroup (Semigroup)
import "duoids" Control.Duoidal
  ( Duoidal,
    DuoidalIO,
    Parallel (Parallel),
    Sequential (Sequential),
    getParallel,
    getSequential,
    liftA2,
    liftIO,
    pure,
    return,
    (=<<),
  )
import "duoids" Control.Duoidal qualified as Duoidal
import "duoids" Data.Duoid (Duoid, sempty, (>->), (|-|))
import "duoids" Data.Duoid qualified as Duoid (Normal)
import "transformers" Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
import "transformers" Control.Monad.Trans.Writer (WriterT (WriterT), runWriterT)
import "this" Control.Duoidal.Trans.Class (DuoidalTrans, lift)
import "this" Control.Duoidal.Trans.Class qualified as Duoidal (NormalTrans)

instance (Semigroup e, Duoidal m) => Applicative (Parallel (ExceptT e m)) where
  pure = Parallel . lift . pure
  liftA2 f (Parallel (ExceptT a)) (Parallel (ExceptT b)) =
    Parallel
      . ExceptT
      . fmap getParallel
      . getParallel
      . getCompose
      . Base.liftA2 f (Compose . Parallel $ fmap Parallel a)
      . Compose
      . Parallel
      $ fmap Parallel b

instance (Semigroup e, Duoidal m) => Applicative (Sequential (ExceptT e m)) where
  pure = Sequential . ExceptT . return . return
  liftA2 f (Sequential (ExceptT a)) (Sequential (ExceptT b)) =
    Sequential
      . ExceptT
      . fmap getSequential
      . getSequential
      . getCompose
      . Base.liftA2 f (Compose . Sequential $ fmap Sequential a)
      . Compose
      . Sequential
      $ fmap Sequential b

instance (Semigroup e, Duoidal m) => Monad (Sequential (ExceptT e m)) where
  (Sequential (ExceptT m)) >>= k =
    Sequential . ExceptT $
      either (return . Left) (runExceptT . getSequential . k) =<< m

instance (Semigroup e, DuoidalIO m) => DuoidalIO (ExceptT e m) where
  liftIO = lift . liftIO

instance (Semigroup e) => DuoidalTrans (ExceptT e) where
  lift = ExceptT . fmap pure

instance (Semigroup e, Duoidal m) => Duoidal.Normal (ExceptT e m)

instance (Semigroup e) => Duoidal.NormalTrans (ExceptT e)

instance (Duoid w, Duoidal m) => Applicative (Parallel (WriterT w m)) where
  pure = Parallel . lift . pure
  Parallel (WriterT f) <*> Parallel (WriterT v) = Parallel . WriterT $ liftA2 k f v
    where
      k ~(a, w) ~(b, w') = (a b, w |-| w')

instance (Duoid w, Duoidal m) => Applicative (Sequential (WriterT w m)) where
  pure = Sequential . lift . return
  Sequential (WriterT f) <*> Sequential (WriterT v) = Sequential . WriterT $ liftA2 k f v
    where
      k ~(a, w) ~(b, w') = (a b, w >-> w')

instance (Duoid w, Duoidal m) => Monad (Sequential (WriterT w m)) where
  (Sequential (WriterT m)) >>= k = Sequential . WriterT $ Duoidal.do
    ~(a, w) <- m
    ~(b, w') <- runWriterT . getSequential $ k a
    return (b, w >-> w')

instance (Duoid w, DuoidalIO m) => DuoidalIO (WriterT w m) where
  liftIO = lift . liftIO

instance (Duoid w) => DuoidalTrans (WriterT w) where
  lift m = WriterT $ fmap (,sempty) m

instance (Duoidal.Normal m, Duoid.Normal w) => Duoidal.Normal (WriterT w m)

instance (Duoid.Normal w) => Duoidal.NormalTrans (WriterT w)