diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+# 0.1.0.0 (November 21st, 2017)
+
+- Initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017 CJ Affiliate by Conversant
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# monad-io-adapter [![Build Status](https://travis-ci.org/cjdev/monad-io-adapter.svg?branch=master)](https://travis-ci.org/cjdev/monad-io-adapter)
+
+This Haskell package provides utilities for converting between computations parameterized via two different typeclasses, both of which can be used to abstract over monad transformer stacks with `IO` at the base. Unfortunately, both classes are frequently used in the Haskell ecosystem, since they have minor differences:
+
+  - `MonadIO` comes from the `base` package (as of `base` version 4.9.0.0), and it provides a `liftIO` operation. It is an extremely simple typeclass, focusing exclusively on lifting `IO` actions through transformer stacks with `IO` at the base.
+
+  - `MonadBase` comes from the `transformers-base` package, and it is a generalized version of `MonadIO`. It provides a more general `liftBase` function, which allows lifting to an arbitrary base monad.
+
+    Generally, this additional power isn’t especially useful, but `MonadBase` appears most often through `MonadBaseControl`, a subclass from the `monad-control` package that enables lifting operations that accept an action in negative position. This class has no `IO`-specialized equivalent (not directly, at least), so it often appears in lifted “control” operations.
+
+Due to these typeclasses being unrelated, it’s not entirely uncommon to end up with type signatures like `(MonadIO m, MonadBaseControl IO m) => ...`, which are a little silly, since `MonadBaseControl IO` really includes all the power of `MonadIO`.
+
+For more information, [see the documentation on Hackage][monad-io-adapter].
+
+[monad-io-adapter]: https://hackage.haskell.org/package/monad-io-adapter
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,7 @@
+-- This script is used to build and install your package. Typically you don't
+-- need to change it. The Cabal documentation has more information about this
+-- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.
+import qualified Distribution.Simple
+
+main :: IO ()
+main = Distribution.Simple.defaultMain
diff --git a/monad-io-adapter.cabal b/monad-io-adapter.cabal
new file mode 100644
--- /dev/null
+++ b/monad-io-adapter.cabal
@@ -0,0 +1,75 @@
+-- This file has been generated from package.yaml by hpack version 0.18.1.
+--
+-- see: https://github.com/sol/hpack
+
+name:           monad-io-adapter
+version:        0.1.0.0
+synopsis:       Adapters between MonadIO and MonadBase IO.
+description:    This package provides utilities for converting between computations
+                parameterized via two different typeclasses 'MonadIO' and 'MonadBase', both of
+                which can be used to abstract over monad transformer stacks with 'IO' at the
+                base. Unfortunately, both classes are frequently used in the Haskell
+                ecosystem, since they have minor differences.
+                .
+                Due to these typeclasses being unrelated, it’s not entirely uncommon to end up
+                with type signatures like @('MonadIO' m, 'MonadBaseControl' 'IO' m) => ...@,
+                which are a little silly, since @'MonadBaseControl' 'IO'@ really includes all
+                the power of 'MonadIO'.
+                .
+                To help alleviate this problem, this package provides a set of utilities for
+                converting between the two constraints in situations where possible.
+category:       Control
+homepage:       https://github.com/cjdev/monad-io-adapter#readme
+bug-reports:    https://github.com/cjdev/monad-io-adapter/issues
+author:         Alexis King <lexi.lambda@gmail.com>
+maintainer:     Alexis King <lexi.lambda@gmail.com>
+copyright:      2017 CJ Affiliate by Conversant
+license:        ISC
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+extra-source-files:
+    CHANGELOG.md
+    package.yaml
+    README.md
+    stack.yaml
+
+source-repository head
+  type: git
+  location: https://github.com/cjdev/monad-io-adapter
+
+library
+  hs-source-dirs:
+      src
+  default-extensions: FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables StandaloneDeriving TupleSections TypeApplications TypeOperators
+  ghc-options: -Wall
+  build-depends:
+      base >= 4.9 && < 5
+    , exceptions >= 0.6 && < 1
+    , monad-control >= 1 && < 2
+    , monad-logger
+    , mtl
+    , transformers
+    , transformers-base
+  exposed-modules:
+      Control.Monad.IO.Adapter
+  other-modules:
+      Paths_monad_io_adapter
+  default-language: Haskell2010
+
+test-suite monad-io-adapter-test-suite
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs:
+      test
+  default-extensions: FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables StandaloneDeriving TupleSections TypeApplications TypeOperators
+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+  build-depends:
+      base
+    , hspec
+    , monad-io-adapter
+    , transformers-base
+  other-modules:
+      Control.Monad.IO.AdapterSpec
+  default-language: Haskell2010
diff --git a/package.yaml b/package.yaml
new file mode 100644
--- /dev/null
+++ b/package.yaml
@@ -0,0 +1,72 @@
+name: monad-io-adapter
+version: 0.1.0.0
+category: Control
+synopsis: Adapters between MonadIO and MonadBase IO.
+description: |
+  This package provides utilities for converting between computations
+  parameterized via two different typeclasses 'MonadIO' and 'MonadBase', both of
+  which can be used to abstract over monad transformer stacks with 'IO' at the
+  base. Unfortunately, both classes are frequently used in the Haskell
+  ecosystem, since they have minor differences.
+
+  Due to these typeclasses being unrelated, it’s not entirely uncommon to end up
+  with type signatures like @('MonadIO' m, 'MonadBaseControl' 'IO' m) => ...@,
+  which are a little silly, since @'MonadBaseControl' 'IO'@ really includes all
+  the power of 'MonadIO'.
+
+  To help alleviate this problem, this package provides a set of utilities for
+  converting between the two constraints in situations where possible.
+
+copyright: 2017 CJ Affiliate by Conversant
+license: ISC
+author: Alexis King <lexi.lambda@gmail.com>
+maintainer: Alexis King <lexi.lambda@gmail.com>
+github: cjdev/monad-io-adapter
+
+extra-source-files:
+- CHANGELOG.md
+- package.yaml
+- README.md
+- stack.yaml
+
+ghc-options: -Wall
+default-extensions:
+- FlexibleContexts
+- FlexibleInstances
+- FunctionalDependencies
+- GADTs
+- GeneralizedNewtypeDeriving
+- KindSignatures
+- LambdaCase
+- MultiParamTypeClasses
+- RankNTypes
+- ScopedTypeVariables
+- StandaloneDeriving
+- TupleSections
+- TypeApplications
+- TypeOperators
+
+library:
+  dependencies:
+  - base >= 4.9 && < 5
+  - exceptions >= 0.6 && < 1
+  - monad-control >= 1 && < 2
+  - monad-logger
+  - mtl
+  - transformers
+  - transformers-base
+  source-dirs: src
+
+tests:
+  monad-io-adapter-test-suite:
+    dependencies:
+    - base
+    - hspec
+    - monad-io-adapter
+    - transformers-base
+    ghc-options:
+    - -rtsopts
+    - -threaded
+    - -with-rtsopts=-N
+    main: Main.hs
+    source-dirs: test
diff --git a/src/Control/Monad/IO/Adapter.hs b/src/Control/Monad/IO/Adapter.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/IO/Adapter.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+{-|
+  This module provides utilities for converting between computations
+  parameterized via two different typeclasses, both of which can be used to
+  abstract over monad transformer stacks with 'IO' at the base. Unfortunately,
+  both classes are frequently used in the Haskell ecosystem, since they have
+  minor differences:
+
+    * 'MonadIO' comes from the @base@ package (as of @base@ version 4.9.0.0),
+      and it provides a 'liftIO' operation. It is an extremely simple typeclass,
+      focusing exclusively on lifting 'IO' actions through transformer stacks
+      with 'IO' at the base.
+
+    * 'MonadBase' comes from the @transformers-base@ package, and it is a
+      generalized version of 'MonadIO'. It provides a more general 'liftBase'
+      function, which allows lifting to an arbitrary base monad.
+
+        Generally, this additional power isn’t especially useful, but
+        'MonadBase' appears most often through 'MonadBaseControl', a subclass
+        from the @monad-control@ package that enables lifting operations that
+        accept an action in negative position. This class has no
+        'IO'-specialized equivalent (not directly, at least), so it often
+        appears in lifted “control” operations.
+
+  Due to these typeclasses being unrelated, it’s not entirely uncommon to end up
+  with type signatures like @('MonadIO' m, 'MonadBaseControl' 'IO' m) => ...@,
+  which are a little silly, since @'MonadBaseControl' 'IO'@ really includes all
+  the power of 'MonadIO'.
+
+  To help alleviate this problem, this module provides a set of utilities for
+  converting between the two constraints in situations where possible. The
+  'MonadIOAdapterT' transformer implements 'MonadIO' in terms of @'MonadBase'
+  'IO'@, and the 'MonadBaseIOAdapterT' transformer implements @'MonadBase' 'IO'@
+  in terms of 'MonadIO'.
+-}
+module Control.Monad.IO.Adapter
+  ( -- * Adapting MonadIO in terms of MonadBase IO
+    MonadIOAdapterT(MonadIOAdapterT)
+  , adaptMonadIO
+    -- * Adapting MonadBase IO in terms of MonadIO
+  , MonadBaseIOAdapterT(MonadBaseIOAdapterT)
+  , adaptMonadBaseIO
+  ) where
+
+import Control.Monad.Base (MonadBase, liftBase)
+import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
+import Control.Monad.Except (MonadError)
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import Control.Monad.Logger (MonadLogger)
+import Control.Monad.Reader (MonadReader)
+import Control.Monad.State (MonadState)
+import Control.Monad.Trans.Class (MonadTrans)
+import Control.Monad.Trans.Control
+  ( ComposeSt, MonadBaseControl(..), MonadTransControl(..)
+  , defaultLiftBaseWith, defaultLiftWith, defaultRestoreM, defaultRestoreT )
+import Control.Monad.Trans.Identity (IdentityT(..))
+import Control.Monad.Writer (MonadWriter)
+
+
+{-|
+A transformer that implements 'MonadIO' in terms of @'MonadBase' 'IO'@, simply
+converting any calls to 'liftIO' into calls to 'liftBase'. This makes it
+possible to discharge 'MonadIO' constraints by converting them into
+@'MonadBase' 'IO'@ constraints. For example, given a value with the following
+type:
+
+@
+foo :: 'MonadIO' m => m ()
+@
+
+…it’s possible to convert its type signature using 'adaptMonadIO':
+
+@
+'adaptMonadIO' foo :: 'MonadBase' 'IO' m => m ()
+@
+-}
+newtype MonadIOAdapterT m a = MonadIOAdapterT' (IdentityT m a)
+  deriving ( Functor, Applicative, Monad, MonadTrans, MonadBase b
+           , MonadReader r, MonadWriter w, MonadState s, MonadError e
+           , MonadThrow, MonadCatch, MonadMask
+           , MonadLogger )
+
+instance MonadTransControl MonadIOAdapterT where
+  type StT MonadIOAdapterT a = StT IdentityT a
+  liftWith = defaultLiftWith MonadIOAdapterT' (\(MonadIOAdapterT' x) -> x)
+  {-# INLINE liftWith #-}
+  restoreT = defaultRestoreT MonadIOAdapterT'
+  {-# INLINE restoreT #-}
+
+instance MonadBaseControl b m => MonadBaseControl b (MonadIOAdapterT m) where
+  type StM (MonadIOAdapterT m) a = ComposeSt MonadIOAdapterT m a
+  liftBaseWith = defaultLiftBaseWith
+  {-# INLINE liftBaseWith #-}
+  restoreM = defaultRestoreM
+  {-# INLINE restoreM #-}
+
+instance MonadBase IO m => MonadIO (MonadIOAdapterT m) where
+  liftIO = liftBase
+  {-# INLINE liftIO #-}
+
+pattern MonadIOAdapterT :: m a -> MonadIOAdapterT m a
+pattern MonadIOAdapterT x = MonadIOAdapterT' (IdentityT x)
+#if MIN_VERSION_GLASGOW_HASKELL(8,2,1,0)
+{-# COMPLETE MonadIOAdapterT #-}
+#endif
+
+-- | “Runs” a 'MonadIOAdapterT' computation and returns the remaining
+-- transformer stack.
+adaptMonadIO :: MonadIOAdapterT m a -> m a
+adaptMonadIO (MonadIOAdapterT' (IdentityT x)) = x
+{-# INLINE adaptMonadIO #-}
+
+
+{-|
+A transformer that implements 'MonadBase' in terms of 'MonadIO', simply
+converting any calls to 'liftBase' into calls to 'liftIO'. This makes it
+possible to discharge @'MonadBase' 'IO'@ constraints by converting them into
+'MonadIO' constraints. For example, given a value with the following type:
+
+@
+foo :: 'MonadBase' 'IO' m => m ()
+@
+
+…it’s possible to convert its type signature using 'adaptMonadIO':
+
+@
+'adaptMonadBaseIO' foo :: 'MonadIO' m => m ()
+@
+-}
+newtype MonadBaseIOAdapterT m a = MonadBaseIOAdapterT' (IdentityT m a)
+  deriving ( Functor, Applicative, Monad, MonadTrans, MonadIO
+           , MonadReader r, MonadWriter w, MonadState s, MonadError e
+           , MonadThrow, MonadCatch, MonadMask
+           , MonadLogger )
+
+instance MonadIO m => MonadBase IO (MonadBaseIOAdapterT m) where
+  liftBase = liftIO
+  {-# INLINE liftBase #-}
+
+pattern MonadBaseIOAdapterT :: m a -> MonadBaseIOAdapterT m a
+pattern MonadBaseIOAdapterT x = MonadBaseIOAdapterT' (IdentityT x)
+#if MIN_VERSION_GLASGOW_HASKELL(8,2,1,0)
+{-# COMPLETE MonadIOAdapterT #-}
+#endif
+
+-- | “Runs” a 'MonadBaseIOAdapterT' computation and returns the remaining
+-- transformer stack.
+adaptMonadBaseIO :: MonadBaseIOAdapterT m a -> m a
+adaptMonadBaseIO (MonadBaseIOAdapterT' (IdentityT x)) = x
+{-# INLINE adaptMonadBaseIO #-}
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,66 @@
+# This file was automatically generated by 'stack init'
+#
+# Some commonly used options have been documented as comments in this file.
+# For advanced use and comprehensive documentation of the format, please see:
+# https://docs.haskellstack.org/en/stable/yaml_configuration/
+
+# Resolver to choose a 'specific' stackage snapshot or a compiler version.
+# A snapshot resolver dictates the compiler version and the set of packages
+# to be used for project dependencies. For example:
+#
+# resolver: lts-3.5
+# resolver: nightly-2015-09-21
+# resolver: ghc-7.10.2
+# resolver: ghcjs-0.1.0_ghc-7.10.2
+# resolver:
+#  name: custom-snapshot
+#  location: "./custom-snapshot.yaml"
+resolver: lts-9.14
+
+# User packages to be built.
+# Various formats can be used as shown in the example below.
+#
+# packages:
+# - some-directory
+# - https://example.com/foo/bar/baz-0.0.2.tar.gz
+# - location:
+#    git: https://github.com/commercialhaskell/stack.git
+#    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
+#   extra-dep: true
+#  subdirs:
+#  - auto-update
+#  - wai
+#
+# A package marked 'extra-dep: true' will only be built if demanded by a
+# non-dependency (i.e. a user package), and its test suites and benchmarks
+# will not be run. This is useful for tweaking upstream packages.
+packages:
+- .
+# Dependency packages to be pulled from upstream that are not in the resolver
+# (e.g., acme-missiles-0.3)
+# extra-deps: []
+
+# Override default flag values for local packages and extra-deps
+# flags: {}
+
+# Extra package databases containing global packages
+# extra-package-dbs: []
+
+# Control whether we use the GHC we find on the path
+# system-ghc: true
+#
+# Require a specific version of stack, using version ranges
+# require-stack-version: -any # Default
+# require-stack-version: ">=1.6"
+#
+# Override the architecture used by stack, especially useful on Windows
+# arch: i386
+# arch: x86_64
+#
+# Extra directories used by stack for building
+# extra-include-dirs: [/path/to/dir]
+# extra-lib-dirs: [/path/to/dir]
+#
+# Allow a newer minor version of GHC than the snapshot specifies
+# compiler-check: newer-minor
diff --git a/test/Control/Monad/IO/AdapterSpec.hs b/test/Control/Monad/IO/AdapterSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Control/Monad/IO/AdapterSpec.hs
@@ -0,0 +1,21 @@
+module Control.Monad.IO.AdapterSpec (spec) where
+
+import Control.Monad.Base (MonadBase)
+import Control.Monad.IO.Class (MonadIO)
+import Test.Hspec
+
+import Control.Monad.IO.Adapter
+
+spec :: Spec
+spec = do
+  describe "MonadIOAdapterT" $
+    it "converts a MonadIO constraint into MonadBase IO" $
+      (adaptMonadIO (pure () :: forall n. MonadIO n => n ())
+          :: forall m. MonadBase IO m => m ())
+        `shouldReturn` ()
+
+  describe "MonadBaseIOAdapterT" $
+    it "converts a MonadBase IO constraint into MonadIO" $
+      (adaptMonadBaseIO (pure () :: forall n. MonadBase IO n => n ())
+          :: forall m. MonadIO m => m ())
+        `shouldReturn` ()
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
