diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+# Changelog for variadic
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Estatico Studios LLC (c) 2021
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Estatico Studios LLC nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Main where
+
+import Criterion.Main
+import Data.Maybe
+import Control.Monad.Morph
+import qualified Control.Variadic
+import qualified Control.Variadic.Bench.NoReader
+
+#define BENCH(M) \
+  [ bench "vhoist" $ nf id $ \
+      let go = M.vhoist listToMaybe f \
+      in run go \
+  , bench "monad" $ nf id $ \
+      let go = M.fromVariadicT do { \
+            x <- M.toVariadicT f; \
+            y <- M.toVariadicT g; \
+            z <- lift [x, y]; \
+            pure z; } \
+        in run go \
+  ]
+
+main :: IO ()
+main = defaultMain
+  [ bgroup "Variadic" (BENCH(Control.Variadic))
+  , bgroup "NoReader" (BENCH(Control.Variadic.Bench.NoReader))
+  ]
+  where
+  f  x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 =
+    [x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35]
+
+  g  x35 x34 x33 x32 x31 x30 x29 x28 x27 x26 x25 x24 x23 x22 x21 x20 x19 x18 x17 x16 x15 x14 x13 x12 x11 x10 x9 x8 x7 x6 x5 x4 x3 x2 x1 x0 =
+    [x35,x34,x33,x32,x31,x30,x29,x28,x27,x26,x25,x24,x23,x22,x21,x20,x19,x18,x17,x16,x15,x14,x13,x12,x11,x10,x9,x8,x7,x6,x5,x4,x3,x2,x1,x0]
+
+  run h =
+    h '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z'
diff --git a/bench/Control/Variadic/Bench/NoReader.hs b/bench/Control/Variadic/Bench/NoReader.hs
new file mode 100644
--- /dev/null
+++ b/bench/Control/Variadic/Bench/NoReader.hs
@@ -0,0 +1,180 @@
+-- | 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
diff --git a/bench/Control/Variadic/Bench/NoReader/Generic.hs b/bench/Control/Variadic/Bench/NoReader/Generic.hs
new file mode 100644
--- /dev/null
+++ b/bench/Control/Variadic/Bench/NoReader/Generic.hs
@@ -0,0 +1,7 @@
+module Control.Variadic.Bench.NoReader.Generic
+  ( ghoist
+  , ghoist0
+  , ghoist'
+  ) where
+
+import Control.Variadic.Bench.NoReader.Generic.Internal
diff --git a/bench/Control/Variadic/Bench/NoReader/Generic/Internal.hs b/bench/Control/Variadic/Bench/NoReader/Generic/Internal.hs
new file mode 100644
--- /dev/null
+++ b/bench/Control/Variadic/Bench/NoReader/Generic/Internal.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+module Control.Variadic.Bench.NoReader.Generic.Internal where
+
+import Control.Monad.Morph (MFunctor)
+import Control.Variadic.Bench.NoReader
+import Data.Kind (Constraint)
+import Data.Proxy (Proxy(Proxy))
+import GHC.Generics
+import GHC.TypeLits
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function, ignoring
+-- the @close@ field, if it exists.
+ghoist
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g '["close"]
+     )
+  => (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist = ghoist' (Proxy @'["close"])
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function; no fields
+-- are ignored.
+ghoist0
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g '[]
+     )
+  => (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist0 = ghoist' (Proxy @'[])
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function.
+-- A supplied of @ignored@ fields is provided to signal which
+-- fields should not be transformed.
+ghoist'
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g ignored
+     )
+  => proxy ignored
+  -> (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist' proxy f = to . gghoist proxy f . from
+
+class GHoist (i :: * -> *) (o :: * -> *) (f :: * -> *) (g :: * -> *) (ignored :: [Symbol]) where
+  gghoist :: proxy ignored -> (forall x. f x -> g x) -> i p -> o p
+
+instance (GHoist i o f g ignored) => GHoist (M1 D c i) (M1 D c o) f g ignored where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance (GHoist i o f g ignored) => GHoist (M1 C c i) (M1 C c o) f g ignored where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance {-# OVERLAPPING #-}
+  ( VerifyIgnored n a ignored
+  ) => GHoist
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R a))
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R a))
+        f g ignored
+  where
+  gghoist _proxy _f (M1 i) = M1 i
+
+instance
+  ( GHoist (K1 R i) (K1 R o) f g ignored
+  , VerifyNotIgnored n i ignored
+  ) => GHoist
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R i))
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R o))
+        f g ignored
+  where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance
+  ( GHoist i1 o1 f g ignored
+  , GHoist i2 o2 f g ignored
+  ) => GHoist (i1 :*: i2) (o1 :*: o2) f g ignored
+  where
+  gghoist proxy f (i1 :*: i2) = gghoist proxy f i1 :*: gghoist proxy f i2
+
+instance
+  ( Monad f
+  , MFunctor (VariadicT args)
+  , IsVariadicT vf args f a
+  , IsVariadicT vg args g a
+  ) => GHoist (K1 R vf) (K1 R vg) f g ignored
+  where
+  gghoist _proxy f (K1 vf) = K1 (vhoist f vf)
+
+type VerifyIgnored e a es = VerifyIgnoredGo e a es es
+
+type family VerifyIgnoredGo e a es orig :: Constraint where
+  VerifyIgnoredGo x a (x ': xs) orig = ()
+  VerifyIgnoredGo y a (x ': xs) orig = VerifyIgnoredGo y a xs orig
+  VerifyIgnoredGo x a '[] orig       =
+    TypeError
+      ( 'Text "Field:"
+          ':$$: 'Text "  "
+          ':<>: 'Text x
+          ':<>: 'Text " :: "
+          ':<>: 'ShowType a
+          ':$$: 'Text "cannot be ghoist-ed with the supplied "
+          ':<>: 'Text "function and was not in the ignored fields list: "
+          ':<>: 'ShowType orig
+      )
+
+type VerifyNotIgnored e a es = VerifyNotIgnoredGo e a es es
+
+type family VerifyNotIgnoredGo e a es orig :: Constraint where
+  VerifyNotIgnoredGo x a (x ': xs) orig =
+    TypeError
+      ( 'Text "Field:"
+          ':$$: 'Text "  "
+          ':<>: 'Text x
+          ':<>: 'Text " :: "
+          ':<>: 'ShowType a
+          ':$$: 'Text "must be ghoist-ed but was present in the ignored "
+          ':<>: 'Text "fields list: "
+          ':<>: 'ShowType orig
+      )
+
+  VerifyNotIgnoredGo y a (x ': xs) orig = VerifyNotIgnoredGo y a xs orig
+  VerifyNotIgnoredGo x a '[] orig = ()
diff --git a/src/Control/Variadic.hs b/src/Control/Variadic.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Variadic.hs
@@ -0,0 +1,190 @@
+{-# 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
diff --git a/src/Control/Variadic/Generic.hs b/src/Control/Variadic/Generic.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Variadic/Generic.hs
@@ -0,0 +1,7 @@
+module Control.Variadic.Generic
+  ( ghoist
+  , ghoist0
+  , ghoist'
+  ) where
+
+import Control.Variadic.Generic.Internal
diff --git a/src/Control/Variadic/Generic/Internal.hs b/src/Control/Variadic/Generic/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Variadic/Generic/Internal.hs
@@ -0,0 +1,137 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+module Control.Variadic.Generic.Internal where
+
+import Control.Variadic
+import Data.Kind (Constraint)
+import Data.Proxy (Proxy(Proxy))
+import GHC.Generics
+import GHC.TypeLits
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function, ignoring
+-- the @close@ field, if it exists.
+ghoist
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g '["close"]
+     )
+  => (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist = ghoist' (Proxy @'["close"])
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function; no fields
+-- are ignored.
+ghoist0
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g '[]
+     )
+  => (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist0 = ghoist' (Proxy @'[])
+
+-- | Runs @hoist@ on the return values each field of @r@
+-- with the given natural transformation function.
+-- A supplied of @ignored@ fields is provided to signal which
+-- fields should not be transformed.
+ghoist'
+  :: ( Generic (r f)
+     , Generic (r g)
+     , GHoist (Rep (r f)) (Rep (r g)) f g ignored
+     )
+  => proxy ignored
+  -> (forall x. f x -> g x)
+  -> r f
+  -> r g
+ghoist' proxy f = to . gghoist proxy f . from
+
+class GHoist (i :: * -> *) (o :: * -> *) (f :: * -> *) (g :: * -> *) (ignored :: [Symbol]) where
+  gghoist :: proxy ignored -> (forall x. f x -> g x) -> i p -> o p
+
+instance (GHoist i o f g ignored) => GHoist (M1 D c i) (M1 D c o) f g ignored where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance (GHoist i o f g ignored) => GHoist (M1 C c i) (M1 C c o) f g ignored where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance {-# OVERLAPPING #-}
+  ( VerifyIgnored n a ignored
+  ) => GHoist
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R a))
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R a))
+        f g ignored
+  where
+  gghoist _proxy _f (M1 i) = M1 i
+
+instance
+  ( GHoist (K1 R i) (K1 R o) f g ignored
+  , VerifyNotIgnored n i ignored
+  ) => GHoist
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R i))
+        (M1 S ('MetaSel ('Just n) su ss ds) (K1 R o))
+        f g ignored
+  where
+  gghoist proxy f (M1 i) = M1 (gghoist proxy f i)
+
+instance
+  ( GHoist i1 o1 f g ignored
+  , GHoist i2 o2 f g ignored
+  ) => GHoist (i1 :*: i2) (o1 :*: o2) f g ignored
+  where
+  gghoist proxy f (i1 :*: i2) = gghoist proxy f i1 :*: gghoist proxy f i2
+
+instance
+  ( Monad f
+  , IsVariadic vf args (f a)
+  , IsVariadic vg args (g a)
+  ) => GHoist (K1 R vf) (K1 R vg) f g ignored
+  where
+  gghoist _proxy f (K1 vf) = K1 (vhoist f vf)
+
+type VerifyIgnored e a es = VerifyIgnoredGo e a es es
+
+type family VerifyIgnoredGo e a es orig :: Constraint where
+  VerifyIgnoredGo x a (x ': xs) orig = ()
+  VerifyIgnoredGo y a (x ': xs) orig = VerifyIgnoredGo y a xs orig
+  VerifyIgnoredGo x a '[] orig       =
+    TypeError
+      ( 'Text "Field:"
+          ':$$: 'Text "  "
+          ':<>: 'Text x
+          ':<>: 'Text " :: "
+          ':<>: 'ShowType a
+          ':$$: 'Text "cannot be ghoist-ed with the supplied "
+          ':<>: 'Text "function and was not in the ignored fields list: "
+          ':<>: 'ShowType orig
+      )
+
+type VerifyNotIgnored e a es = VerifyNotIgnoredGo e a es es
+
+type family VerifyNotIgnoredGo e a es orig :: Constraint where
+  VerifyNotIgnoredGo x a (x ': xs) orig =
+    TypeError
+      ( 'Text "Field:"
+          ':$$: 'Text "  "
+          ':<>: 'Text x
+          ':<>: 'Text " :: "
+          ':<>: 'ShowType a
+          ':$$: 'Text "must be ghoist-ed but was present in the ignored "
+          ':<>: 'Text "fields list: "
+          ':<>: 'ShowType orig
+      )
+
+  VerifyNotIgnoredGo y a (x ': xs) orig = VerifyNotIgnoredGo y a xs orig
+  VerifyNotIgnoredGo x a '[] orig = ()
diff --git a/src/Control/Variadic/Varargs.hs b/src/Control/Variadic/Varargs.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Variadic/Varargs.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+module Control.Variadic.Varargs where
+
+-- | Glorified HList representing variadic arguments.
+data family Varargs (l :: [*])
+data instance Varargs '[] = Nil
+data instance Varargs (x ': xs) = x `Cons` Varargs xs
+infixr 2 `Cons`
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,166 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Main where
+
+import Control.Monad.IO.Class (MonadIO(liftIO))
+import Control.Monad.Reader (ReaderT, ask, runReaderT)
+import Control.Variadic
+import Control.Variadic.Generic (ghoist)
+import Data.Char (chr, ord)
+import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef, writeIORef)
+import Data.Map.Strict (Map)
+import Data.Maybe (isJust, listToMaybe)
+import Test.Hspec (HasCallStack, SpecWith, describe, hspec)
+import Test.Hspec.Expectations.Lifted (shouldBe, shouldReturn)
+import Test.Infra.Handle (Handle(Handle))
+import Test.ShouldNotCompile (shouldNotCompile)
+import qualified Data.Map.Strict as Map
+import qualified Test.Hspec as Hspec
+import qualified Test.Infra.Handle
+
+-- | Specialized 'Hspec.it' body to @IO ()@ to make type inference
+-- easier for @hspec-expectations-lifted@.
+it :: (HasCallStack) => String -> IO () -> SpecWith ()
+it = Hspec.it
+
+main :: IO ()
+main = hspec do
+  describe "Variadic" do
+    describe "arity" do
+      it "0" do
+        let v = toVariadic ioA
+        fromVariadic v `shouldReturn` 'a'
+      it "1" do
+        let v = toVariadic ioCharToInt
+        fromVariadic v 'a' `shouldReturn` 97
+      it "2" do
+        let v = toVariadic ioPlusChars
+        fromVariadic v 'a' 2 `shouldReturn` 'c'
+
+  describe "VariadicT" do
+    it "*>" do
+      let v1 = toVariadicT ioCharToInt
+      let v2 = toVariadicT ioIncChar
+      let v3 = v1 *> v2
+      fromVariadicT v3 'a' `shouldReturn` 'b'
+
+  describe "Functions" do
+    it "...*>" do
+      ref <- newIORef '\0'
+      let f = writeIORef ref ...*> ioIncChar
+      f 'a' `shouldReturn` 'b'
+      readIORef ref `shouldReturn` 'a'
+
+    it "vhoist" do
+      let f = vhoist listToMaybe twoList
+      f 1 2 `shouldBe` Just 1
+
+  describe "Generic" do
+    it "errors" do
+      shouldNotCompile "TestCompilerErrors"
+
+    it "ghoist" do
+      -- Keep track of how many times we call 'close'.
+      closeCounter <- newIORef (0 :: Int)
+
+      -- A starting implementation of 'Handle'.
+      let readerHandle :: Handle (ReaderT DB IO)
+          readerHandle = Handle
+            { insert = \name descr -> do
+                db <- ask
+                liftIO $ atomicModifyIORef' db \rows ->
+                  let k = maybe 1 (+1) $ fmap fst $ Map.lookupMax rows
+                   in ( Map.insert k (name, descr) rows
+                      , k
+                      )
+
+            , get = \k -> do
+                db <- ask
+                rows <- liftIO $ readIORef db
+                pure $ Map.lookup k rows
+
+            , delete = \k -> do
+                db <- ask
+                liftIO $ atomicModifyIORef' db \rows ->
+                  let (oldValue, rows') = Map.updateLookupWithKey mempty k rows
+                   in (rows', isJust oldValue)
+
+            , close = atomicModifyIORef' closeCounter \i -> (i + 1, ())
+            }
+
+      -- Create the initial database.
+      db :: DB <- newIORef mempty
+
+      -- Tests for the @readerHandle@.
+      flip runReaderT db do
+        let Handle { insert, get, delete, close } = readerHandle
+        get 1 `shouldReturn` Nothing
+        insert "foo" "bar" `shouldReturn` 1
+        get 1 `shouldReturn` Just ("foo", "bar")
+        get 2 `shouldReturn` Nothing
+        insert "spam" "eggs" `shouldReturn` 2
+        get 2 `shouldReturn` Just ("spam", "eggs")
+        delete 2 `shouldReturn` True
+        get 2 `shouldReturn` Nothing
+        delete 2 `shouldReturn` False
+        liftIO do
+          readIORef closeCounter `shouldReturn` 0
+          close
+          readIORef closeCounter `shouldReturn` 1
+
+      -- Used to keep track of how many times our @runner@ is invoked.
+      runCounter <- newIORef (0 :: Int)
+
+      -- Natural transformation for converting our @readerHandle@ to @ioHandle@.
+      let runner :: ReaderT DB IO x -> IO x
+          runner action = do
+            atomicModifyIORef' runCounter \i -> (i + 1, ())
+            runReaderT action db
+
+      -- Example usage of 'ghoist'.
+      let ioHandle :: Handle IO
+          ioHandle = ghoist runner readerHandle
+
+      -- Tests for the @ioHandle@.
+      do
+        let Handle { insert, get, delete, close } = ioHandle
+        readIORef runCounter `shouldReturn` 0
+        get 1 `shouldReturn` Just ("foo", "bar")
+        readIORef runCounter `shouldReturn` 1
+        get 2 `shouldReturn` Nothing
+        readIORef runCounter `shouldReturn` 2
+        k <- insert "baz" "quux"
+        readIORef runCounter `shouldReturn` 3
+        k `shouldBe` 2
+        get 2 `shouldReturn` Just ("baz", "quux")
+        readIORef runCounter `shouldReturn` 4
+        delete 2 `shouldReturn` True
+        readIORef runCounter `shouldReturn` 5
+        get 2 `shouldReturn` Nothing
+        readIORef runCounter `shouldReturn` 6
+        -- Calling 'close' should not increment the @runCounter@
+        -- but should still increment @closeCounter@.
+        readIORef closeCounter `shouldReturn` 1
+        close
+        readIORef closeCounter `shouldReturn` 2
+        readIORef runCounter `shouldReturn` 6
+
+ioA :: IO Char
+ioA = pure 'a'
+
+ioCharToInt :: Char -> IO Int
+ioCharToInt c = pure (ord c)
+
+ioIncChar :: Char -> IO Char
+ioIncChar c = pure (chr (ord c + 1))
+
+ioPlusChars :: Char -> Int -> IO Char
+ioPlusChars c n = pure (chr (ord c + n))
+
+twoList :: Int -> Int -> [Int]
+twoList x y = [x, y]
+
+-- | A simple in-memory database.
+type DB = IORef (Map Int (String, String))
diff --git a/test/Test/Infra/Handle.hs b/test/Test/Infra/Handle.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Infra/Handle.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE KindSignatures #-}
+module Test.Infra.Handle where
+
+import GHC.Generics (Generic)
+
+-- | An example handle for interacting with a database.
+-- We'll be using it as a test case for @ghoist@.
+data Handle (f :: * -> *) = Handle
+  { insert :: String -> String -> f Int
+  , get :: Int -> f (Maybe (String, String))
+  , delete :: Int -> f Bool
+  , close :: IO ()
+  } deriving stock (Generic)
diff --git a/test/Test/ShouldNotCompile.hs b/test/Test/ShouldNotCompile.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/ShouldNotCompile.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralisedNewtypeDeriving #-}
+module Test.ShouldNotCompile where
+
+import Control.Monad (when)
+import Data.Maybe (fromMaybe)
+import System.Environment (lookupEnv)
+import System.Exit (ExitCode(ExitSuccess))
+import System.Process (readProcessWithExitCode)
+import Test.Hspec (HasCallStack, expectationFailure, shouldBe)
+import qualified Data.List as List
+
+-- | Test support for asserting that expressions should not compile.
+--
+-- Attempts to compile the supplied @moduleName@ in @testdata/${moduleName}.hs@ and
+-- asserts that it both fails to compile and reports error messages that match
+-- the contents of @testdata/${moduleName}.txt@.
+--
+-- You may be tempted to use @-fdefer-type-errors@ and/or the
+-- @should-not-typecheck@ package; however, this doesn't work for custom type errors.
+-- See: https://gitlab.haskell.org/ghc/ghc/-/issues/18310
+shouldNotCompile :: (HasCallStack) => String -> IO ()
+shouldNotCompile moduleName = do
+  stackExe <- fromMaybe "stack" <$> lookupEnv "STACK_EXE"
+  (exitCode, _out, err) <- readProcessWithExitCode
+    stackExe
+    [ "ghc"
+    , "--"
+    , "testdata/" <> moduleName <> ".hs"
+    , "-fno-code"
+    ]
+    ""
+  when (exitCode == ExitSuccess) do
+    expectationFailure $ "Unexpected compilation success for module " <> moduleName
+  let actual = CompileError $ unlines $ dropWhile skipLine $ lines err
+  expected <- fmap CompileError $ readFile $ "testdata/" <> moduleName <> ".txt"
+  actual `shouldBe` expected
+  where
+  skipLine line =
+    "Stack has not been tested with" `List.isPrefixOf` line
+      || null line
+
+newtype CompileError = CompileError String
+  deriving newtype (Eq)
+
+instance Show CompileError where
+  show (CompileError s) = s
diff --git a/testdata/TestCompilerErrors.hs b/testdata/TestCompilerErrors.hs
new file mode 100644
--- /dev/null
+++ b/testdata/TestCompilerErrors.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE TypeApplications #-}
+module TestCompilerErrors where
+
+import Control.Monad.Reader (ReaderT, runReaderT)
+import Control.Variadic.Generic (ghoist, ghoist', ghoist0)
+import Data.Proxy (Proxy(Proxy))
+import GHC.Generics (Generic)
+
+data Handle1 f = Handle1
+  { a1 :: f ()
+  , b1 :: IO ()
+  } deriving stock (Generic)
+
+readerHandle1 :: Handle1 (ReaderT () IO)
+readerHandle1 = Handle1
+  { a1 = pure ()
+  , b1 = pure ()
+  }
+
+ioHandle1 :: Handle1 IO
+ioHandle1 = ghoist (flip runReaderT ()) readerHandle1
+
+data Handle2 f = Handle2
+  { a2 :: f ()
+  , b2 :: f ()
+  } deriving stock (Generic)
+
+readerHandle2 :: Handle2 (ReaderT () IO)
+readerHandle2 = Handle2
+  { a1 = pure ()
+  , b2 = pure ()
+  }
+
+ioHandle2 :: Handle2 IO
+ioHandle2 = ghoist' (Proxy @'["b2"]) (flip runReaderT ()) readerHandle2
+
+data Handle3 f = Handle3
+  { a3 :: f ()
+  , b3 :: IO ()
+  } deriving stock (Generic)
+
+readerHandle3 :: Handle3 (ReaderT () IO)
+readerHandle3 = Handle3
+  { a3 = pure ()
+  , b3 = pure ()
+  }
+
+ioHandle3 :: Handle3 IO
+ioHandle3 = ghoist0 (flip runReaderT ()) readerHandle3
diff --git a/testdata/TestCompilerErrors.txt b/testdata/TestCompilerErrors.txt
new file mode 100644
--- /dev/null
+++ b/testdata/TestCompilerErrors.txt
@@ -0,0 +1,43 @@
+testdata/TestCompilerErrors.hs:24:13: error:
+    • Field:
+        b1 :: IO ()
+      cannot be ghoist-ed with the supplied function and was not in the ignored fields list: '["close"]
+    • In the expression: ghoist (flip runReaderT ()) readerHandle1
+      In an equation for ‘ioHandle1’:
+          ioHandle1 = ghoist (flip runReaderT ()) readerHandle1
+   |
+24 | ioHandle1 = ghoist (flip runReaderT ()) readerHandle1
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+testdata/TestCompilerErrors.hs:32:17: error:
+    • Constructor ‘Handle2’ does not have field ‘a1’
+    • In the expression: Handle2 {a1 = pure (), b2 = pure ()}
+      In an equation for ‘readerHandle2’:
+          readerHandle2 = Handle2 {a1 = pure (), b2 = pure ()}
+   |
+32 | readerHandle2 = Handle2
+   |                 ^^^^^^^...
+
+testdata/TestCompilerErrors.hs:38:13: error:
+    • Field:
+        b2 :: ReaderT () IO ()
+      must be ghoist-ed but was present in the ignored fields list: '["b2"]
+    • In the expression:
+        ghoist' (Proxy @'["b2"]) (flip runReaderT ()) readerHandle2
+      In an equation for ‘ioHandle2’:
+          ioHandle2
+            = ghoist' (Proxy @'["b2"]) (flip runReaderT ()) readerHandle2
+   |
+38 | ioHandle2 = ghoist' (Proxy @'["b2"]) (flip runReaderT ()) readerHandle2
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+testdata/TestCompilerErrors.hs:52:13: error:
+    • Field:
+        b3 :: IO ()
+      cannot be ghoist-ed with the supplied function and was not in the ignored fields list: '[]
+    • In the expression: ghoist0 (flip runReaderT ()) readerHandle3
+      In an equation for ‘ioHandle3’:
+          ioHandle3 = ghoist0 (flip runReaderT ()) readerHandle3
+   |
+52 | ioHandle3 = ghoist0 (flip runReaderT ()) readerHandle3
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/variadic.cabal b/variadic.cabal
new file mode 100644
--- /dev/null
+++ b/variadic.cabal
@@ -0,0 +1,86 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: d8b0b953a94896493afb01fb7d8f965e391cbb6caf5757a5293a10e8469a85e4
+
+name:           variadic
+version:        0.0.0.0
+synopsis:       Abstractions for working with variadic functions
+description:    Please see the README on GitHub at <https://github.com/estatico/variadic#readme>
+category:       Control
+homepage:       https://github.com/estatico/variadic#readme
+bug-reports:    https://github.com/estatico/variadic/issues
+author:         Cary Robbins
+maintainer:     carymrobbins@gmail.com
+copyright:      2021 Estatico Studios LLC
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    CHANGELOG.md
+    testdata/TestCompilerErrors.hs
+    testdata/TestCompilerErrors.txt
+
+source-repository head
+  type: git
+  location: https://github.com/estatico/variadic
+
+library
+  exposed-modules:
+      Control.Variadic
+      Control.Variadic.Generic
+      Control.Variadic.Generic.Internal
+      Control.Variadic.Varargs
+  other-modules:
+      Paths_variadic
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >=4.7 && <5
+    , mmorph >=1.1.3 && <1.2
+    , mtl >=2.2.2 && <2.3
+  default-language: Haskell2010
+
+test-suite variadic-test
+  type: exitcode-stdio-1.0
+  main-is: Test.hs
+  other-modules:
+      Test.Infra.Handle
+      Test.ShouldNotCompile
+      Paths_variadic
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , containers
+    , hspec
+    , hspec-expectations-lifted
+    , mmorph >=1.1.3 && <1.2
+    , mtl >=2.2.2 && <2.3
+    , process
+    , variadic
+  default-language: Haskell2010
+
+benchmark variadic-benchmark
+  type: exitcode-stdio-1.0
+  main-is: Bench.hs
+  other-modules:
+      Control.Variadic.Bench.NoReader
+      Control.Variadic.Bench.NoReader.Generic
+      Control.Variadic.Bench.NoReader.Generic.Internal
+      Paths_variadic
+  hs-source-dirs:
+      bench
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -O2
+  build-depends:
+      base >=4.7 && <5
+    , criterion
+    , mmorph >=1.1.3 && <1.2
+    , mtl >=2.2.2 && <2.3
+    , variadic
+  default-language: Haskell2010
