diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for lift-transformers
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2021 Manuel Bärenz
+
+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/examples/Examples/StateSig.hs b/examples/Examples/StateSig.hs
new file mode 100644
--- /dev/null
+++ b/examples/Examples/StateSig.hs
@@ -0,0 +1,35 @@
+module Examples.StateSig where
+
+import "base" Control.Monad (when)
+
+import "operational" Control.Monad.Operational
+
+import "has-transformers" Control.Monad.Trans.Has
+import "has-transformers" Control.Monad.Trans.Has.State
+
+data StateSig s a where
+  Put :: s -> StateSig s ()
+  Get :: StateSig s s
+
+type StateSigT s m a = ProgramT (StateSig s) m a
+
+type HasStateSig s m = Has (ProgramT (StateSig s)) m
+
+putS :: HasStateSig s m => s -> m ()
+-- putS = liftH . singleton . Put
+putS s = liftH $ singleton $ Put s
+
+getS :: HasStateSig s m => m s
+getS = liftH $ singleton Get
+
+program :: (HasStateSig Bool m, Monad m) => m ()
+program = do
+  b <- getS
+  when b $ putS False
+
+interpretStateSig :: HasState s m => StateSig s a -> m a
+interpretStateSig (Put s) = put s
+interpretStateSig Get = get
+
+interpretStateSigT :: (HasState s m, Monad m) => StateSigT s m a -> m a
+interpretStateSigT = interpretWithMonadT interpretStateSig
diff --git a/examples/Examples/TwoReaders.hs b/examples/Examples/TwoReaders.hs
new file mode 100644
--- /dev/null
+++ b/examples/Examples/TwoReaders.hs
@@ -0,0 +1,30 @@
+module Examples.TwoReaders where
+
+import "has-transformers" Control.Monad.Trans.Has.Reader
+
+-- | Our "business logic" program using two 'ReaderT' effects.
+--   Note that we use a type signature to help the type checker understand
+--   which environment is which.
+program ::
+  (Monad m, HasReader Int m, HasReader Bool m) =>
+  m (Int, Bool)
+program = do
+  envInt <- ask
+  envBool <- ask
+  return (envInt, envBool)
+
+-- | The 'Int' environment we are going to supply
+envInt :: Int
+envInt = 23
+
+-- | The 'Bool' environment we are going to supply
+envBool :: Bool
+envBool = True
+
+-- | For documentation, this is the stack that will be handled.
+--   (But we don't actually ever use this type alias.)
+type Stack a = ReaderT Int (ReaderT Bool IO) a
+
+-- | The 'program' with its environments supplied
+handled :: IO (Int, Bool)
+handled = program `runReaderT` envInt `runReaderT` envBool
diff --git a/examples/Examples/TwoReadersSpec.hs b/examples/Examples/TwoReadersSpec.hs
new file mode 100644
--- /dev/null
+++ b/examples/Examples/TwoReadersSpec.hs
@@ -0,0 +1,11 @@
+module Examples.TwoReadersSpec where
+
+import "hspec" Test.Hspec
+
+import Examples.TwoReaders
+
+spec :: Spec
+spec = do
+  describe "two-Reader stack" $ do
+    it "returns the environments" $ do
+      handled `shouldReturn` (envInt, envBool)
diff --git a/examples/Spec.hs b/examples/Spec.hs
new file mode 100644
--- /dev/null
+++ b/examples/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/has-transformers.cabal b/has-transformers.cabal
new file mode 100644
--- /dev/null
+++ b/has-transformers.cabal
@@ -0,0 +1,65 @@
+cabal-version:      2.4
+name:               has-transformers
+version:            0.1.0.0
+
+synopsis: This library `Has` transformers
+
+description:
+  A very slim library for first-order effects based on monad transformers
+  (and nearly nothing else).
+  .
+  Given a transformer stack @t1 (t2 (t3 (... m))) a@,
+  you can automatically lift any function @thing :: tN m a@ into the stack with a single function, 'liftH'.
+
+bug-reports: https://github.com/turion/has-transformers/issues
+license: MIT
+license-file: LICENSE
+author: Manuel Bärenz
+maintainer: programming@manuelbaerenz.de
+
+copyright: (c) 2021- Manuel Bärenz
+category: Effect
+extra-source-files: CHANGELOG.md
+
+library
+  exposed-modules:
+    Control.Monad.Trans.Has
+    Control.Monad.Trans.Has.Accum
+    Control.Monad.Trans.Has.Except
+    Control.Monad.Trans.Has.Reader
+    Control.Monad.Trans.Has.State
+    Control.Monad.Trans.Has.Writer
+  default-extensions:
+      ConstraintKinds
+    , FlexibleContexts
+    , FlexibleInstances
+    , ImportQualifiedPost
+    , MultiParamTypeClasses
+    , PackageImports
+  build-depends:
+      base ^>= 4.14.3.0
+    , transformers
+  hs-source-dirs: src
+  default-language: Haskell2010
+
+test-suite examples
+  type: exitcode-stdio-1.0
+  default-language: Haskell2010
+  main-is: Spec.hs
+  hs-source-dirs: examples
+  other-modules:
+    Examples.StateSig
+    Examples.TwoReaders
+    Examples.TwoReadersSpec
+  default-extensions:
+      ConstraintKinds
+    , FlexibleContexts
+    , GADTs
+    , ImportQualifiedPost
+    , PackageImports
+  build-depends:
+      base ^>= 4.14.3.0
+    , has-transformers
+    , hspec >= 2.9.0
+    , operational
+  build-tool-depends: hspec-discover:hspec-discover
diff --git a/src/Control/Monad/Trans/Has.hs b/src/Control/Monad/Trans/Has.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE KindSignatures #-}
+module Control.Monad.Trans.Has where
+
+import "transformers" Control.Monad.Trans.Class
+
+-- |
+-- class Has t m where
+--   liftH :: t Identity a -> m a
+
+{- | The transformer stack @m@ contains the transformer @t@.
+
+Explicitly, @m = t1 (t2 (t3 ... (tN m)...))@,
+and @t@ is one of these @t1, t2, ...@s.
+-}
+class Has (t :: (* -> *) -> * -> *) m where
+  {- | Insert an action of this transformer into an arbitrary position in the stack.
+
+  This will apply 'lift' as many times as necessary to insert the action.
+  The higher-rank type involving @forall n@ basically says:
+  "The action to lift must only use the structure of the _transformer_,
+  not of a specific monad,
+  and is thus definable for any monad @n@".
+  -}
+  liftH :: (forall n . Monad n => t n a) -> m a
+
+-- | If the transformer is outermost,
+--   the action can be inserted as-is.
+instance Monad m => Has t (t m) where
+  liftH = id
+  {-# INLINE liftH #-}
+
+-- | If the target transformer @t@ is under a different layer @t1@, 'lift' once.
+instance {-# Overlappable #-} (Monad m, MonadTrans t1, Has t m) => Has t (t1 m) where
+  liftH action = lift $ liftH action
+  {-# INLINE liftH #-}
diff --git a/src/Control/Monad/Trans/Has/Accum.hs b/src/Control/Monad/Trans/Has/Accum.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has/Accum.hs
@@ -0,0 +1,17 @@
+module Control.Monad.Trans.Has.Accum
+  ( module Control.Monad.Trans.Has.Accum
+  , module X
+  ) where
+
+import "transformers" Control.Monad.Trans.Accum
+  qualified as Accum
+import "transformers" Control.Monad.Trans.Accum
+  as X
+  (AccumT (..))
+
+import "this" Control.Monad.Trans.Has
+
+type HasAccum w m = Has (AccumT w) m
+
+add :: HasAccum w m => w -> m ()
+add w = liftH $ Accum.add w
diff --git a/src/Control/Monad/Trans/Has/Except.hs b/src/Control/Monad/Trans/Has/Except.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has/Except.hs
@@ -0,0 +1,18 @@
+module Control.Monad.Trans.Has.Except
+  ( module Control.Monad.Trans.Has.Except
+  , module X
+  ) where
+
+import "transformers" Control.Monad.Trans.Except
+  qualified as Except
+import "transformers" Control.Monad.Trans.Except
+  as X
+  (ExceptT (..))
+
+import "this" Control.Monad.Trans.Has
+import qualified Control.Monad.Trans.Except as Control.Monad.Trans.Has
+
+type HasExcept e m = Has (ExceptT e) m
+
+throw :: HasExcept e m => e -> m ()
+throw e = liftH $ ExceptT $ return $ Left e
diff --git a/src/Control/Monad/Trans/Has/Reader.hs b/src/Control/Monad/Trans/Has/Reader.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has/Reader.hs
@@ -0,0 +1,16 @@
+module Control.Monad.Trans.Has.Reader
+  ( module Control.Monad.Trans.Has.Reader
+  , module X
+  ) where
+
+import "transformers" Control.Monad.Trans.Reader
+  qualified as Reader
+import "transformers" Control.Monad.Trans.Reader
+  as X (ReaderT(..))
+
+import "this" Control.Monad.Trans.Has
+
+type HasReader r m = Has (ReaderT r) m
+
+ask :: HasReader r m => m r
+ask = liftH Reader.ask
diff --git a/src/Control/Monad/Trans/Has/State.hs b/src/Control/Monad/Trans/Has/State.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has/State.hs
@@ -0,0 +1,19 @@
+module Control.Monad.Trans.Has.State
+  ( module Control.Monad.Trans.Has.State
+  , module X
+  ) where
+
+import "transformers" Control.Monad.Trans.State.Strict
+  qualified as State
+import "transformers" Control.Monad.Trans.State.Strict
+  as X (StateT(..))
+
+import "this" Control.Monad.Trans.Has
+
+type HasState s m = Has (StateT s) m
+
+get :: HasState s m => m s
+get = liftH State.get
+
+put :: HasState s m => s -> m ()
+put s = liftH $ State.put s
diff --git a/src/Control/Monad/Trans/Has/Writer.hs b/src/Control/Monad/Trans/Has/Writer.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Trans/Has/Writer.hs
@@ -0,0 +1,17 @@
+module Control.Monad.Trans.Has.Writer
+  ( module Control.Monad.Trans.Has.Writer
+  , module X
+  ) where
+
+import "transformers" Control.Monad.Trans.Writer.Strict
+  qualified as Writer
+import "transformers" Control.Monad.Trans.Writer.Strict
+  as X
+  (WriterT (..))
+
+import "this" Control.Monad.Trans.Has
+
+type HasWriter w m = Has (WriterT w) m
+
+tell :: HasWriter w m => w -> m ()
+tell w = liftH $ Writer.tell w
