packages feed

variadic-0.0.0.0: bench/Control/Variadic/Bench/NoReader.hs

-- | Re-implementation of @Control.Variadic@ which does not use @ReaderT@
-- and, as such, does not pack the argument list into @Varargs@. While
-- this may seem to be a more efficient encoding, the benchmarks
-- don't seem to prove this out.
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Control.Variadic.Bench.NoReader where

import Data.Coerce (Coercible, coerce)
import Control.Monad.Morph (MFunctor(hoist), MMonad(embed), MonadTrans(lift))

type family ToVariadicArgs x :: [*] where
  ToVariadicArgs (a -> x) = a ': ToVariadicArgs x
  ToVariadicArgs a = '[]

type family ToVariadicReturn x :: * where
  ToVariadicReturn (a -> x) = ToVariadicReturn x
  ToVariadicReturn a = a

type family Signature (args :: [*]) r where
  Signature '[] r = r
  Signature (x ': xs) r = x -> Signature xs r

type IsVariadic x args r =
  ( ToVariadicArgs x ~ args
  , ToVariadicReturn x ~ r
  , Coercible x (Variadic args r)
  , Signature args r ~ x
  )

toVariadic
  :: (IsVariadic x args a)
  => x -> Variadic args a
toVariadic = coerce

type IsVariadicT x args f r =
  ( ToVariadicArgs x ~ args
  , ToVariadicReturn x ~ f r
  , Coercible x (VariadicT args f r)
  , Signature args (f r) ~ x
  )

toVariadicT
  :: (IsVariadicT x args f a)
  => x -> VariadicT args f a
toVariadicT = coerce

newtype Variadic (args :: [*]) (a :: *) = Variadic
  { runVariadic :: Signature args a
  }

newtype VariadicT (args :: [*]) (f :: * -> *) (a :: *) = VariadicT
  { runVariadicT :: Signature args (f a)
  }

-- Added to make compatible with the Control.Variadic module.
fromVariadicT :: VariadicT args f a -> Signature args (f a)
fromVariadicT = runVariadicT

-- Added to make compatible with the Control.Variadic module.
fromVariadic :: Variadic args a -> Signature args a
fromVariadic = runVariadic

instance (Functor f) => Functor (VariadicT '[] f) where
  fmap f (VariadicT x) = VariadicT $ fmap f x {-# INLINE fmap #-}

instance (Functor (VariadicT args f)) => Functor (VariadicT (arg ': args) f) where
  fmap f (VariadicT x) =
    VariadicT \arg -> runVariadicT $
      fmap f $ VariadicT @args @f $ x arg
  {-# INLINE fmap #-}

instance (Applicative f) => Applicative (VariadicT '[] f) where
  pure a = VariadicT $ pure a
  {-# INLINE pure #-}

  VariadicT x <*> VariadicT y = VariadicT $ x <*> y
  {-# INLINE (<*>) #-}

instance (Applicative (VariadicT args f)) => Applicative (VariadicT (arg ': args) f) where
  pure a = VariadicT \_ -> runVariadicT $ pure @(VariadicT args f) a
  {-# INLINE pure #-}

  (<*>)
    :: forall a b.
       VariadicT (arg ': args) f (a -> b)
    -> VariadicT (arg ': args) f a
    -> VariadicT (arg ': args) f b
  VariadicT x <*> VariadicT y =
    VariadicT \arg -> runVariadicT $
      VariadicT @args @f @(a -> b) (x arg) <*> VariadicT @args @f @a (y arg)
  {-# INLINE (<*>) #-}

instance (Monad m) => Monad (VariadicT '[] m) where
  VariadicT x >>= f =
    VariadicT $ x >>= \a -> runVariadicT $ f a

instance (Monad (VariadicT args m)) => Monad (VariadicT (arg ': args) m) where
  (>>=)
    :: forall a b.
       VariadicT (arg ': args) m a
    -> (a -> VariadicT (arg ': args) m b)
    -> VariadicT (arg ': args) m b
  VariadicT x >>= f =
    VariadicT \arg -> runVariadicT $
      VariadicT @args @m @a (x arg) >>= \a ->
        VariadicT @args @m @b $ runVariadicT (f a) arg

instance MFunctor (VariadicT '[]) where
  hoist f (VariadicT x) = VariadicT $ f x
  {-# INLINE hoist #-}

instance (MFunctor (VariadicT args)) => MFunctor (VariadicT (arg ': args)) where
  hoist
    :: forall m n b. (Monad m)
    => (forall a. m a -> n a)
    -> VariadicT (arg ': args) m b
    -> VariadicT (arg ': args) n b
  hoist f (VariadicT x) =
    VariadicT \arg -> runVariadicT $
      hoist f $ VariadicT @args @m @b $ x arg
  {-# INLINE hoist #-}

instance MMonad (VariadicT '[]) where
  embed f (VariadicT x) = f x
  {-# INLINE embed #-}

instance (MMonad (VariadicT args)) => MMonad (VariadicT (arg ': args)) where
  embed
    :: forall m n b. (Monad n)
    => (forall a. m a -> VariadicT (arg ': args) n a)
    -> VariadicT (arg ': args) m b
    -> VariadicT (arg ': args) n b
  embed f (VariadicT x) =
    VariadicT \arg ->
      runVariadicT $
        embed
        (\ma -> VariadicT @args @n (runVariadicT (f ma) arg))
        (VariadicT @args @m @b (x arg))
  {-# INLINE embed #-}

instance MonadTrans (VariadicT '[]) where
  lift ma = VariadicT ma
  {-# INLINE lift #-}

instance (MonadTrans (VariadicT args)) => MonadTrans (VariadicT (arg ': args)) where
  lift ma = VariadicT \_ -> runVariadicT @args $ lift ma
  {-# INLINE lift #-}

-- | Analogous to '*>' for 'VariadicT' but works on vanilla functions.
(...*>)
  :: forall va vb args m a b.
     ( Applicative (VariadicT args m)
     , IsVariadic va args (m a)
     , IsVariadic vb args (m b)
     )
  => va -> vb -> vb
va ...*> vb = fromVariadicT $ toVariadicT @va @args va *> toVariadicT @vb @args vb

-- | Analogous to 'hoist' for 'VariadicT' but works on vanilla functions.
vhoist
  :: ( Monad f
     , MFunctor (VariadicT args)
     , IsVariadicT vf args f a
     , IsVariadicT vg args g a
     )
  => (forall x. f x -> g x) -> vf -> vg
vhoist f = runVariadicT . hoist f . toVariadicT