variadic-0.0.0.0: src/Control/Variadic.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Control.Variadic where
import Control.Monad.Morph (MFunctor(hoist), MMonad, MonadTrans)
import Control.Monad.Reader (ReaderT(ReaderT))
import Control.Variadic.Varargs (Varargs(Cons, Nil))
import Data.Functor (void)
-- | Same as 'Variadic' but captures the higher-kinded type parameter in the
-- return type. Useful so we can use 'Monad' and friends with 'Variadic'
-- functions.
newtype VariadicT args (m :: * -> *) a = VariadicT
{ unVariadicT :: Variadic args (m a)
} deriving (Functor, Applicative, Monad) via ReaderT (Varargs args) m
deriving (MFunctor, MMonad, MonadTrans) via ReaderT (Varargs args)
-- | Converts a function to a 'VariadicT'. Analogous to 'toVariadic'.
toVariadicT
:: ( ToVariadic x
, args ~ ToVariadicArgs x
, m r ~ ToVariadicReturn x
)
=> x -> VariadicT args m r
toVariadicT = VariadicT . toVariadic
-- | Converts a 'VariadicT' to a normal function. Analogous to 'fromVariadic'.
fromVariadicT
:: ( FromVariadic args (m r)
)
=> VariadicT args m r -> FromVariadicSignature args (m r)
fromVariadicT = fromVariadic . unVariadicT
-- | A function whose argument list is collapsed into 'Varargs'
-- and shows its return type.
newtype Variadic args a = Variadic
{ runVariadic :: Varargs args -> a
}
-- | Resolves the argument list for a function of arbitrary arity.
type family ToVariadicArgs x :: [*] where
ToVariadicArgs (i -> o) = i ': ToVariadicArgs o
ToVariadicArgs a = '[]
-- | Resolves the return type for a function of arbitrary arity.
type family ToVariadicReturn x :: * where
ToVariadicReturn (i -> o) = ToVariadicReturn o
ToVariadicReturn a = a
-- | Converts a function of arbitrary arity to 'Variadic'.
class ToVariadic x where
toVariadic :: x -> Variadic (ToVariadicArgs x) (ToVariadicReturn x)
instance {-# OVERLAPPING #-}
( ToVariadicArgs a ~ '[]
, ToVariadicReturn a ~ a
) => ToVariadic a where
toVariadic a = Variadic \_ -> a
instance {-# OVERLAPS #-}
( ToVariadic o
, ToVariadicArgs (i -> o) ~ (i ': args)
, ToVariadicArgs o ~ args
, ToVariadicReturn (i -> o) ~ ToVariadicReturn o
) => ToVariadic (i -> o)
where
toVariadic f =
Variadic \(arg `Cons` args) ->
runVariadic (toVariadic (f arg)) args
-- | Builds a function signature given the @args@ and return type @r@.
type family FromVariadicSignature (args :: [*]) (r :: *) :: * where
FromVariadicSignature '[] r = r
FromVariadicSignature (arg ': args) r = arg -> FromVariadicSignature args r
-- | Converts a 'Variadic' to a normal function.
class FromVariadic args r where
fromVariadic :: Variadic args r -> FromVariadicSignature args r
instance FromVariadic '[] a where
fromVariadic v = runVariadic v Nil
instance (FromVariadic args a) => FromVariadic (arg ': args) a where
fromVariadic v arg =
fromVariadic $ Variadic \args ->
runVariadic v (arg `Cons` args)
-- | Convenience constraint enabling variadic.
--
-- @x@ is the Haskell function type,
-- @args@ is a type-level list of arguments,
-- @r@ is the return type.
--
-- Usually you'll want to use these type arguments polymorphically, e.g. -
--
-- > (...*>)
-- > :: ( Applicative m
-- > , IsVariadic va args (m a)
-- > , IsVariadic vb args (m b)
-- > )
-- > => va -> vb -> vb
-- > va ...*> vb = fromVariadicT $ toVariadicT va *> toVariadicT vb
type IsVariadic x args r =
( ToVariadic x
, args ~ ToVariadicArgs x
, r ~ ToVariadicReturn x
, x ~ FromVariadicSignature args r
, FromVariadic args r
)
-- | Analogous to 'fmap' for 'VariadicT' but works on vanilla functions.
vmap
:: ( Functor f
, IsVariadic va args (f a)
, IsVariadic vb args (f b)
)
=> (a -> b) -> va -> vb
vmap f va = fromVariadicT $ fmap f $ toVariadicT va
-- | Analogous to 'fmap' for 'VariadicT' but works on vanilla functions.
(<$>...)
:: ( Functor f
, IsVariadic va args (f a)
, IsVariadic vb args (f b)
)
=> (a -> b) -> va -> vb
(<$>...) = vmap
-- | Analogous to 'void' for 'VariadicT' but works on vanilla functions.
vvoid
:: ( Functor f
, IsVariadic va args (f a)
, IsVariadic vu args (f ())
)
=> va -> vu
vvoid va = fromVariadicT $ void $ toVariadicT va
-- | Analogous to '*>' for 'VariadicT' but works on vanilla functions.
(...*>)
:: ( Applicative m
, IsVariadic va args (m a)
, IsVariadic vb args (m b)
)
=> va -> vb -> vb
va ...*> vb = fromVariadicT $ toVariadicT va *> toVariadicT vb
-- | Analogous to '<*' for 'VariadicT' but works on vanilla functions.
(<*...)
:: ( Applicative m
, IsVariadic va args (m a)
, IsVariadic vb args (m b)
)
=> va -> vb -> va
va <*... vb = fromVariadicT $ toVariadicT va <* toVariadicT vb
-- | Analogous to '>>=' for 'VariadicT' but works on vanilla functions.
(...>>=)
:: ( Monad m
, IsVariadic va args (m a)
, IsVariadic vb args (m b)
)
=> va -> (a -> vb) -> vb
va ...>>= f = fromVariadicT $ toVariadicT va >>= \a -> toVariadicT (f a)
-- | Analogous to '=<<' for 'VariadicT' but works on vanilla functions.
(=<<...)
:: ( Monad m
, IsVariadic va args (m a)
, IsVariadic vb args (m b)
)
=> (a -> vb) -> va -> vb
(=<<...) = flip (...>>=)
-- | Analogous to 'hoist' for 'VariadicT' but works on vanilla functions.
vhoist
:: ( Monad f
, IsVariadic vf args (f a)
, IsVariadic vg args (g a)
)
=> (forall x. f x -> g x) -> vf -> vg
vhoist f = fromVariadicT . hoist f . toVariadicT