packages feed

basic-0.1.0.0: Data/Basic.hs

{-# LANGUAGE TemplateHaskell #-}

module Data.Basic where

import Control.Concurrent.STM (STM)
import Control.Exception (Handler (..))
import Control.Monad (replicateM)
import Control.Monad.Trans.Accum
import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Except
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Select
import Control.Monad.Trans.State.Lazy as L
import Control.Monad.Trans.State.Strict as S
import Control.Monad.Trans.Writer.Lazy as L
import Control.Monad.Trans.Writer.Strict as S
import Control.Monad.ST.Lazy as L (ST)
import Control.Monad.ST.Strict as S (ST)
import Data.Foldable
import Data.Functor.Compose
import Data.Functor.Const
import Data.Functor.Identity
import Data.Functor.Product
import Data.List.NonEmpty (NonEmpty)
import Data.Monoid (Alt (..))
import Data.Proxy
import Data.Semigroup (Arg (..))
import Language.Haskell.TH
import Text.ParserCombinators.ReadP (ReadP)
import Text.ParserCombinators.ReadPrec (ReadPrec)

-- | Laws if @'Functor' f@:
--
-- * @'liftBase' . 'fmap' f = 'fmap' f . 'liftBase'@
class Basic1 (f :: α -> *) where
    type Base f :: α -> *
    liftBase :: Base f a -> f a

$(let f :: Int -> Name -> Q Dec
      f n name =
          [InstanceD Nothing [] (ConT (mkName "Basic1") `AppT` t)
           [TySynInstD ''Base $ TySynEqn [t] t,
            ValD (VarP 'liftBase) (NormalB (VarE 'id)) []]
            | t <- foldl' AppT (ConT name) <$> replicateM n (VarT <$> newName "a") ]
  in traverse (uncurry f) [(0, ''IO), (1, ''L.ST), (1, ''S.ST), (0, ''STM),
                           (0, ''Maybe), (1, ''Either), (0, ''Identity),
                           (0, ''[]), (0, ''NonEmpty),
                           (0, ''Proxy), (1, ''Const),
                           (0, ''ReadP), (0, ''ReadPrec), (0, ''Handler),
                           (1, ''Arg), (1, ''Alt)])

instance Basic1 ((,) a) where
    type Base ((,) a) = (,) a
    liftBase = id

instance Basic1 ((->) a) where
    type Base ((->) a) = (->) a
    liftBase = id

instance (Monad f, Basic1 g) => Basic1 (Compose f g) where
    type Base (Compose f g) = Base g
    liftBase = Compose . pure . liftBase

instance (Basic1 f, Basic1 g, Base f ~ Base g) => Basic1 (Product f g) where
    type Base (Product f g) = Base f
    liftBase = Pair <$> liftBase <*> liftBase

$(let f :: Int -> Name -> Q Dec
      f n name = 
          [InstanceD Nothing [ConT ''Basic1 `AppT` f, ConT ''Monad `AppT` f] (ConT ''Basic1 `AppT` AppT t f)
           [TySynInstD ''Base $ TySynEqn [AppT t f] (ConT ''Base `AppT` f),
            ValD (VarP 'liftBase) (NormalB (foldl' AppE (VarE '(.)) (VarE <$> ['lift, 'liftBase]))) []]
            | t <- foldl' AppT (ConT name) <$> replicateM n (VarT <$> newName "a")
            , f <- VarT <$> newName "f"]
  in traverse (uncurry f) [(0, ''IdentityT), (0, ''MaybeT), (1, ''ContT), (1, ''SelectT),
                           (1, ''ReaderT), (1, ''L.StateT), (1, ''S.StateT), (1, ''ExceptT)])

instance (Basic1 f, Monad f, Monoid a) => Basic1 (L.WriterT a f) where
    type Base (L.WriterT a f) = Base f
    liftBase = lift . liftBase

instance (Basic1 f, Monad f, Monoid a) => Basic1 (S.WriterT a f) where
    type Base (S.WriterT a f) = Base f
    liftBase = lift . liftBase

instance (Basic1 f, Monad f, Monoid a) => Basic1 (AccumT a f) where
    type Base (AccumT a f) = Base f
    liftBase = lift . liftBase