diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Donnacha Oisín Kidney
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# arity-generic-liftA
+
+There's a family of functions in
+[Control.Applicative](https://hackage.haskell.org/package/base-4.11.0.0/docs/Control-Applicative.html)
+which follow the pattern `liftA2`, `liftA3`, etc. Using some
+tricks from Richard Eisenberg's thesis we can write them all at once. This
+package does exactly that, providing a function (`lift`) which is an
+arity-generic version of the `liftAn`.
+
+```haskell
+>>> lift (\x y z -> x ++ y ++ z) (Just "a") (Just "b") (Just "c")
+Just "abc"
+```
+
+[Eisenberg, Richard A. “Dependent Types in Haskell: Theory and Practice.” University of Pennsylvania, 2016.](https://github.com/goldfirere/thesis/raw/master/built/thesis.pdf)
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/arity-generic-liftA.cabal b/arity-generic-liftA.cabal
new file mode 100644
--- /dev/null
+++ b/arity-generic-liftA.cabal
@@ -0,0 +1,52 @@
+-- This file has been generated from package.yaml by hpack version 0.28.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: a5291f7acc496b8e14741d59cc33eeb92e35c60db728f80aae2427f87c8d690e
+
+name:           arity-generic-liftA
+version:        0.1.0.0
+synopsis:       Provides an arity-generic version of the liftA2, liftA3... liftAn functions.
+description:    Please see the README on GitHub at <https://github.com/oisdk/arity-generic-liftA#readme>
+category:       Control
+homepage:       https://github.com/oisdk/arity-generic-liftA#readme
+bug-reports:    https://github.com/oisdk/arity-generic-liftA/issues
+author:         Donnacha Oisín Kidney
+maintainer:     mail@doisinkidney.com
+copyright:      2018 Donnacha Oisín Kidney
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+extra-source-files:
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/oisdk/arity-generic-liftA
+
+library
+  exposed-modules:
+      Control.Applicative.Lift
+      Control.Applicative.Lift.Internal
+  other-modules:
+      Paths_arity_generic_liftA
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <5
+  default-language: Haskell2010
+
+test-suite arity-generic-liftA-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_arity_generic_liftA
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      arity-generic-liftA
+    , base >=4.7 && <5
+    , doctest >=0.3.0
+  default-language: Haskell2010
diff --git a/src/Control/Applicative/Lift.hs b/src/Control/Applicative/Lift.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Applicative/Lift.hs
@@ -0,0 +1,24 @@
+-- |
+-- Module      : Control.Applicative.Lift
+-- Copyright   : (c) Donnacha Oisín Kidney 2018
+-- License     : MIT
+-- Maintainer  : mail@doisinkidney.com
+-- Portability : GHC
+--
+-- There's a family of functions in "Control.Applicative" which follow the
+-- pattern 'Control.Applicative.liftA2', 'Control.Applicative.liftA3', etc.
+-- Using some tricks from Richard Eisenberg's thesis we can write them all at
+-- once.
+--
+--     * Eisenberg, Richard A. \"Dependent Types in Haskell: Theory and
+--       Practice.\" University of Pennsylvania, 2016.
+--       <https://github.com/goldfirere/thesis/raw/master/built/thesis.pdf>
+--
+-- This module exports the one 'lift' function; for the internal
+-- implementation details see "Control.Applicative.Lift.Internal".
+
+module Control.Applicative.Lift
+  (lift)
+  where
+
+import           Control.Applicative.Lift.Internal (lift)
diff --git a/src/Control/Applicative/Lift/Internal.hs b/src/Control/Applicative/Lift/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Applicative/Lift/Internal.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+
+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- |
+-- Module      : Control.Applicative.Lift.Internal
+-- Copyright   : (c) Donnacha Oisín Kidney 2018
+-- License     : MIT
+-- Maintainer  : mail@doisinkidney.com
+-- Portability : GHC
+--
+-- There's a family of functions in "Control.Applicative" which follow the
+-- pattern 'Control.Applicative.liftA2', 'Control.Applicative.liftA3', etc.
+-- Using some tricks from Richard Eisenberg's thesis we can write them all at
+-- once.
+--
+--     * Eisenberg, Richard A. \"Dependent Types in Haskell: Theory and
+--       Practice.\" University of Pennsylvania, 2016.
+--       <https://github.com/goldfirere/thesis/raw/master/built/thesis.pdf>
+module Control.Applicative.Lift.Internal where
+
+-- | Simple implementation of Peano numbers.
+data N = Z | S N
+
+-- | @'AppFunc' f n a@ returns the type of the function @a@ "lifted" over @n@
+-- arguments.
+type family AppFunc f n a where
+  AppFunc f Z a = f a
+  AppFunc f (S n) (a -> b) = f a -> AppFunc f n b
+
+-- | Counts the arguments of a function
+type family CountArgs f where
+  CountArgs (_ -> b) = S (CountArgs b)
+  CountArgs _ = Z
+
+-- | The actual class which constructs the lifted function.
+class (CountArgs a ~ n) => Applyable a n where
+  apply :: Applicative f => f a -> AppFunc f (CountArgs a) a
+
+instance (CountArgs a ~ Z) => Applyable a Z where
+  apply = id
+  {-# INLINE apply #-}
+
+instance Applyable b n => Applyable (a -> b) (S n) where
+  apply f x = apply (f <*> x)
+  {-# INLINE apply #-}
+
+-- | Lift a function over applicative arguments. This function is an
+-- arity-generic version of the functions 'Control.Applicative.liftA2',
+-- 'Control.Applicative.liftA3', etc.
+--
+-- Type inference works best when the function being lifted is
+-- monomorphic:
+--
+-- >>> lift (\x y z -> x ++ y ++ z) (Just "a") (Just "b") (Just "c")
+-- Just "abc"
+--
+-- In these cases, GHC can see the number of arguments the function must
+-- have, and so is able to pick the correct instance for 'Applyable'.
+--
+-- If the function is not monomorphic (for instance '+'), you will need
+-- to give a type signature:
+--
+-- >>> lift ((+) :: Int -> Int -> Int) (Just 1) (Just 2)
+-- Just 3
+--
+-- Alternatively, you can use type applications to monomorphise the
+-- function:
+--
+-- >>> :set -XTypeApplications
+-- >>> lift ((+) @Int) (Just 1) (Just 2)
+-- Just 3
+--
+-- Finally, everything is aggressively inlined, so there should be no
+-- cost to using this function over manually writing
+-- 'Control.Applicative.liftA3' etc.
+lift :: (Applyable b n, Applicative f) => (a -> b) -> (f a -> AppFunc f n b)
+lift f x = apply (fmap f x)
+{-# INLINE lift #-}
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,4 @@
+import Test.DocTest
+
+main :: IO ()
+main = doctest ["-isrc", "src/"]
