diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 0.1.0.0
+
+Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Ilya Kostyuchenko (c) 2020
+
+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 Ilya Kostyuchenko 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# fused-effects-squeal
+
+This is an overview of the way this library works. If you would like to learn how Squeal itself works you should head to the [morphismtech/squeal repository](https://github.com/morphismtech/squeal).
+
+## Usage
+
+There are two sepaeate effects with corresponding carriers: `Squeal` (with `SquealC`) and `SquealPool` (with `SquealPoolC`).
+
+`Squeal` mimics the functions from `MonadPQ` (from `squeal-postgresql`) and represnts the "inside a transaction" effect.
+
+You can run it directly with `runSquealWithConn` family of functions, but you probably want to use a connection pool.
+
+`SquealPool` allows you to call `runSqueal` function, which picks a connection from the connection pool and runs the `Squeal` effect.
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/fused-effects-squeal.cabal b/fused-effects-squeal.cabal
new file mode 100644
--- /dev/null
+++ b/fused-effects-squeal.cabal
@@ -0,0 +1,40 @@
+cabal-version: 1.12
+name: fused-effects-squeal
+version: 0.1.0.0
+license: BSD3
+license-file: LICENSE
+copyright: Ilya Kostyuchenko
+maintainer: ilyakooo0@gmail.com
+author: Ilya Kostyuchenko
+homepage: https://github.com/ilyakooo0/fused-effects-squeal#readme
+synopsis: A fused-effects adapter for squeal-postgresql.
+description:
+    A fused-effects adapter for squeal-postgresql. It allows you to conjure `MonadPQ` instances and manage connection pools in a `fused-effects` stack.
+category: Control
+build-type: Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+library
+    exposed-modules:
+        Control.Carrier.Squeal
+        Control.Effect.Squeal
+    hs-source-dirs: src
+    other-modules:
+        Control.Carrier.Orphans
+        Paths_fused_effects_squeal
+    default-language: Haskell2010
+    default-extensions: DataKinds DeriveFunctor DeriveGeneric
+                        FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving
+                        MultiParamTypeClasses OverloadedLabels OverloadedStrings
+                        TypeApplications TypeOperators UndecidableInstances
+                        ScopedTypeVariables PolyKinds DerivingStrategies StandaloneDeriving
+                        LambdaCase
+    build-depends:
+        base >=4.7 && <5,
+        fused-effects >=1.0.0.0 && <1.1,
+        squeal-postgresql >=0.5.2.0 && <0.6,
+        unliftio >=0.2.12 && <0.3,
+        unliftio-core >=0.1.2.0 && <0.2,
+        unliftio-pool >=0.2.1.0 && <0.3
diff --git a/src/Control/Carrier/Orphans.hs b/src/Control/Carrier/Orphans.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Carrier/Orphans.hs
@@ -0,0 +1,45 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Control.Carrier.Orphans
+  (
+  )
+where
+
+import Control.Carrier.Interpret
+import Control.Carrier.Lift
+import Control.Carrier.Reader
+import qualified Control.Carrier.Trace.Ignoring as TI
+import qualified Control.Carrier.Trace.Printing as TP
+import Control.Monad.IO.Unlift
+
+instance MonadUnliftIO m => MonadUnliftIO (LiftC m) where
+  askUnliftIO = LiftC $ withUnliftIO $ \u -> return (UnliftIO (unliftIO u . runM))
+  {-# INLINE askUnliftIO #-}
+
+  withRunInIO inner = LiftC $ withRunInIO $ \run' -> inner (run' . runM)
+  {-# INLINE withRunInIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (ReaderC r m) where
+  askUnliftIO = ReaderC $ \r -> withUnliftIO $ \u -> pure (UnliftIO (\(ReaderC x) -> unliftIO u (x r)))
+  {-# INLINE askUnliftIO #-}
+
+  withRunInIO inner = ReaderC $ \r -> withRunInIO $ \go -> inner (go . runReader r)
+  {-# INLINE withRunInIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (InterpretC s sig m) where
+  askUnliftIO = InterpretC $ withUnliftIO $ \u -> return (UnliftIO (\(InterpretC m) -> unliftIO u m))
+  {-# INLINE askUnliftIO #-}
+  withRunInIO inner = InterpretC $ withRunInIO $ \run' -> inner (\(InterpretC m) -> run' m)
+  {-# INLINE withRunInIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (TI.TraceC m) where
+  askUnliftIO = TI.TraceC $ withUnliftIO $ \u -> return (UnliftIO (unliftIO u . TI.runTrace))
+  {-# INLINE askUnliftIO #-}
+  withRunInIO inner = TI.TraceC $ withRunInIO $ \run' -> inner (run' . TI.runTrace)
+  {-# INLINE withRunInIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (TP.TraceC m) where
+  askUnliftIO = TP.TraceC $ withUnliftIO $ \u -> return (UnliftIO (unliftIO u . TP.runTrace))
+  {-# INLINE askUnliftIO #-}
+  withRunInIO inner = TP.TraceC $ withRunInIO $ \run' -> inner (run' . TP.runTrace)
+  {-# INLINE withRunInIO #-}
diff --git a/src/Control/Carrier/Squeal.hs b/src/Control/Carrier/Squeal.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Carrier/Squeal.hs
@@ -0,0 +1,175 @@
+module Control.Carrier.Squeal
+  ( DBConnection,
+    runSquealWithConn,
+    runSquealWithConnRethrow,
+    runSquealWithConn',
+    SquealC (..),
+    getSquealPool,
+    runSqueal',
+    runSqueal,
+    runSquealPool,
+    runSquealRethrow,
+    module Control.Algebra,
+  )
+where
+
+import Control.Algebra
+import Control.Carrier.Orphans ()
+import Control.Effect.Squeal
+import Control.Monad.IO.Unlift
+import qualified Squeal.PostgreSQL as Sq
+import UnliftIO
+import UnliftIO.Pool
+
+newtype SquealC schemas m k = SquealC {unSquealC :: DBConnection schemas -> m k}
+
+instance Functor m => Functor (SquealC schemas m) where
+  fmap f (SquealC mk) = SquealC $ (fmap . fmap) f mk
+  {-# INLINE fmap #-}
+
+instance Applicative m => Applicative (SquealC schemas m) where
+  pure x = SquealC $ \_ -> pure x
+  {-# INLINE pure #-}
+  (SquealC mklhs) <*> (SquealC mkrhs) = SquealC $ \r -> mklhs r <*> mkrhs r
+  {-# INLINE (<*>) #-}
+
+instance Monad m => Monad (SquealC schemas m) where
+  (SquealC mk) >>= f = SquealC $ \r -> mk r >>= (runSquealWithConn' r . f)
+  {-# INLINE (>>=) #-}
+
+instance MonadIO m => MonadIO (SquealC schemas m) where
+  liftIO = SquealC . const . liftIO
+  {-# INLINE liftIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (SquealC schemas m) where
+  withRunInIO inner = SquealC $ \r -> withRunInIO (\f -> inner (f . runSquealWithConn' r))
+  {-# INLINE withRunInIO #-}
+
+instance (MonadUnliftIO m, Algebra sig m) => Algebra (Squeal schemas :+: sig) (SquealC schemas m) where
+  alg (L (ManipulateParams man x mk)) = SquealC $ \r -> do
+    res <- flip evalPQ r $ Sq.manipulateParams man x
+    runSquealWithConn' r $ mk res
+  alg (L (TraversePrepared man x mk)) = SquealC $ \r -> do
+    res <- flip evalPQ r $ Sq.traversePrepared man x
+    runSquealWithConn' r $ mk res
+  alg (L (TraversePrepared_ man x mk)) = SquealC $ \r -> do
+    flip evalPQ r $ Sq.traversePrepared_ man x
+    runSquealWithConn' r mk
+  alg (R other) = SquealC $ \r -> alg . hmap (runSquealWithConn' r) $ other
+  {-# INLINE alg #-}
+
+-- | Run a squeal session using the given database connection without a transaction and without any error handling. You probably shouldn't use this.
+runSquealWithConn' :: DBConnection schemas -> SquealC schemas m k -> m k
+runSquealWithConn' r (SquealC mk) = mk r
+{-# INLINE runSquealWithConn' #-}
+
+-- | Run a squeal session using the given database connection, transaction mode and error handler.
+runSquealWithConn ::
+  MonadUnliftIO m =>
+  DBConnection schemas ->
+  Maybe TransactionMode ->
+  (SquealException -> m k) ->
+  SquealC schemas m k ->
+  m k
+runSquealWithConn db tr er mk =
+  handleSqueal er $ maybe id (transactionallyRetry' db) tr (runSquealWithConn' db mk)
+  where
+    transactionallyRetry' ::
+      (MonadUnliftIO m) =>
+      DBConnection schemas ->
+      TransactionMode ->
+      m x ->
+      m x
+    transactionallyRetry' conn mode tx = mask $ \restore ->
+      loop . try $ do
+        x <- restore tx
+        flip evalPQ conn $ Sq.manipulate_ commit
+        return x
+      where
+        loop attempt = do
+          flip evalPQ conn $ Sq.manipulate_ $ begin mode
+          attempt >>= \case
+            Left (PQException (PQState _ (Just "40001") _)) -> do
+              flip evalPQ conn $ Sq.manipulate_ rollback
+              loop attempt
+            Left err -> do
+              flip evalPQ conn $ Sq.manipulate_ rollback
+              throwIO err
+            Right x -> return x
+{-# INLINE runSquealWithConn #-}
+
+-- | Run a squeal session using the given database connection and transaction mode. Errors will not be handled.
+runSquealWithConnRethrow ::
+  MonadUnliftIO m =>
+  DBConnection schemas ->
+  Maybe TransactionMode ->
+  SquealC schemas m k ->
+  m k
+runSquealWithConnRethrow db tr = runSquealWithConn db tr throwIO
+{-# INLINE runSquealWithConnRethrow #-}
+
+newtype SquealPoolC schemas m k = SquealPoolC {unSquealPoolC :: Pool (DBConnection schemas) -> m k}
+
+instance Functor m => Functor (SquealPoolC schemas m) where
+  fmap f (SquealPoolC mk) = SquealPoolC $ (fmap . fmap) f mk
+  {-# INLINE fmap #-}
+
+instance Applicative m => Applicative (SquealPoolC schemas m) where
+  pure x = SquealPoolC $ const $ pure x
+  {-# INLINE pure #-}
+  (SquealPoolC mklhs) <*> (SquealPoolC mkrhs) = SquealPoolC $ \r -> mklhs r <*> mkrhs r
+  {-# INLINE (<*>) #-}
+
+instance Monad m => Monad (SquealPoolC schemas m) where
+  (SquealPoolC mk) >>= f = SquealPoolC $ \r -> mk r >>= (($ r) . unSquealPoolC . f)
+  {-# INLINE (>>=) #-}
+
+instance MonadIO m => MonadIO (SquealPoolC schemas m) where
+  liftIO = SquealPoolC . const . liftIO
+  {-# INLINE liftIO #-}
+
+instance MonadUnliftIO m => MonadUnliftIO (SquealPoolC schemas m) where
+  withRunInIO inner = SquealPoolC $ \r -> withRunInIO (\f -> inner (f . ($ r) . unSquealPoolC))
+  {-# INLINE withRunInIO #-}
+
+runSquealPool :: Pool (DBConnection schemas) -> SquealPoolC schemas m k -> m k
+runSquealPool conn (SquealPoolC f) = f conn
+{-# INLINE runSquealPool #-}
+
+instance Algebra sig m => Algebra (SquealPool schemas :+: sig) (SquealPoolC schemas m) where
+  alg (L (GetSquealPool mk)) = SquealPoolC $ \r -> runSquealPool r $ mk r
+  alg (R other) = SquealPoolC $ \r -> alg . hmap (runSquealPool r) $ other
+  {-# INLINE alg #-}
+
+-- | Run a squeal session picking a database connection from the connection pool without a transaction and without any error handling. You probably shouldn't use this.
+runSqueal' ::
+  (MonadUnliftIO m, Has (SquealPool schemas) sig m) =>
+  SquealC schemas m k ->
+  m k
+runSqueal' = runSqueal Nothing throwIO
+{-# INLINE runSqueal' #-}
+
+-- | Run a squeal session picking a database connection from the connection pool with the given transaction mode and error handler.
+runSqueal ::
+  (MonadUnliftIO m, Has (SquealPool schemas) sig m) =>
+  Maybe TransactionMode ->
+  (SquealException -> m k) ->
+  SquealC schemas m k ->
+  m k
+runSqueal tr er mk = do
+  pool <- getSquealPool
+  withResource pool $ \db ->
+    runSquealWithConn db tr er mk
+{-# INLINE runSqueal #-}
+
+-- | Run a squeal session picking a database connection from the connection pool with the given transaction mode. Errors will not be handled.
+runSquealRethrow ::
+  (MonadUnliftIO m, Has (SquealPool schemas) sig m) =>
+  Maybe TransactionMode ->
+  SquealC schemas m k ->
+  m k
+runSquealRethrow tr mk = do
+  pool <- getSquealPool
+  withResource pool $ \db ->
+    runSquealWithConn db tr throwIO mk
+{-# INLINE runSquealRethrow #-}
diff --git a/src/Control/Effect/Squeal.hs b/src/Control/Effect/Squeal.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Effect/Squeal.hs
@@ -0,0 +1,174 @@
+module Control.Effect.Squeal
+  ( Squeal (..),
+    manipulateParams,
+    manipulateParams_,
+    manipulate,
+    manipulate_,
+    runQueryParams,
+    runQuery,
+    traversePrepared,
+    forPrepared,
+    traversePrepared_,
+    forPrepared_,
+
+    -- * Pool
+    DBConnection,
+    SquealPool (..),
+    getSquealPool,
+
+    -- * Reexports
+    module Sq,
+    module Control.Algebra,
+  )
+where
+
+import Control.Algebra
+import Control.Carrier.Orphans ()
+import Data.Functor
+import Squeal.PostgreSQL as Sq hiding
+  ( Has,
+    forPrepared,
+    forPrepared_,
+    manipulate,
+    manipulateParams,
+    manipulateParams_,
+    manipulate_,
+    runQuery,
+    runQueryParams,
+    traversePrepared,
+    traversePrepared_,
+  )
+
+type DBConnection (schemas :: SchemasType) = K Connection schemas
+
+data Squeal (schemas :: SchemasType) m k where
+  ManipulateParams ::
+    Sq.ToParams x params =>
+    Sq.Manipulation '[] schemas params ys ->
+    x ->
+    (Sq.K Sq.Result ys -> m k) ->
+    Squeal schemas m k
+  TraversePrepared ::
+    (Sq.ToParams x params, Traversable list) =>
+    Sq.Manipulation '[] schemas params ys ->
+    list x ->
+    (list (Sq.K Sq.Result ys) -> m k) ->
+    Squeal schemas m k
+  TraversePrepared_ ::
+    (Sq.ToParams x params, Foldable list) =>
+    Sq.Manipulation '[] schemas params '[] ->
+    list x ->
+    m k ->
+    Squeal schemas m k
+
+instance Functor m => Functor (Squeal schemas m) where
+  fmap f (ManipulateParams man x mk) = ManipulateParams man x ((fmap . fmap) f mk)
+  fmap f (TraversePrepared man x mk) = TraversePrepared man x ((fmap . fmap) f mk)
+  fmap f (TraversePrepared_ man x mk) = TraversePrepared_ man x (fmap f mk)
+  {-# INLINE fmap #-}
+
+instance HFunctor (Squeal schemas) where
+  hmap f (ManipulateParams man x mk) = ManipulateParams man x (fmap f mk)
+  hmap f (TraversePrepared man x mk) = TraversePrepared man x (fmap f mk)
+  hmap f (TraversePrepared_ man x mk) = TraversePrepared_ man x (f mk)
+  {-# INLINE hmap #-}
+
+instance Effect (Squeal schemas) where
+  thread ctx f (ManipulateParams man x mk) = ManipulateParams man x $ \y -> f (ctx $> mk y)
+  thread ctx f (TraversePrepared man x mk) = TraversePrepared man x $ \y -> f (ctx $> mk y)
+  thread ctx f (TraversePrepared_ man x mk) = TraversePrepared_ man x $ f (ctx $> mk)
+  {-# INLINE thread #-}
+
+-- | See 'Sq.manipulateParams' from @squeal-postgresql@.
+manipulateParams ::
+  (Has (Squeal schemas) sig m, Sq.ToParams x params) =>
+  Sq.Manipulation '[] schemas params ys ->
+  x ->
+  m (Sq.K Sq.Result ys)
+manipulateParams man x = send $ ManipulateParams man x pure
+
+-- | See 'Sq.manipulateParams_' from @squeal-postgresql@.
+manipulateParams_ ::
+  (Has (Squeal schemas) sig m, Sq.ToParams x params) =>
+  Sq.Manipulation '[] schemas params ys ->
+  x ->
+  m ()
+manipulateParams_ man x = manipulateParams man x $> ()
+
+-- | See 'Sq.manipulate' from @squeal-postgresql@.
+manipulate ::
+  Has (Squeal schemas) sig m =>
+  Sq.Manipulation '[] schemas '[] ys ->
+  m (Sq.K Sq.Result ys)
+manipulate man = manipulateParams man ()
+
+-- | See 'Sq.manipulate_' from @squeal-postgresql@.
+manipulate_ ::
+  Has (Squeal schemas) sig m =>
+  Sq.Manipulation '[] schemas '[] ys ->
+  m ()
+manipulate_ man = manipulate_ man $> ()
+
+-- | See 'Sq.runQueryParams' from @squeal-postgresql@.
+runQueryParams ::
+  (Has (Squeal schemas) sig m, Sq.ToParams x params) =>
+  Sq.Query '[] '[] schemas params ys ->
+  x ->
+  m (Sq.K Sq.Result ys)
+runQueryParams = manipulateParams . Sq.queryStatement
+
+-- | See 'Sq.runQuery' from @squeal-postgresql@.
+runQuery ::
+  Has (Squeal schemas) sig m =>
+  Sq.Query '[] '[] schemas '[] ys ->
+  m (Sq.K Sq.Result ys)
+runQuery q = runQueryParams q ()
+
+-- | See 'Sq.traversePrepared' from @squeal-postgresql@.
+traversePrepared ::
+  (Sq.ToParams x params, Traversable list, Has (Squeal schemas) sig m) =>
+  Sq.Manipulation '[] schemas params ys ->
+  list x ->
+  m (list (Sq.K Sq.Result ys))
+traversePrepared man l = send $ TraversePrepared man l pure
+
+-- | See 'Sq.forPrepared' from @squeal-postgresql@.
+forPrepared ::
+  (Sq.ToParams x params, Traversable list, Has (Squeal schemas) sig m) =>
+  list x ->
+  Sq.Manipulation '[] schemas params ys ->
+  m (list (Sq.K Sq.Result ys))
+forPrepared = flip traversePrepared
+
+-- | See 'Sq.traversePrepared_' from @squeal-postgresql@.
+traversePrepared_ ::
+  (Sq.ToParams x params, Foldable list, Has (Squeal schemas) sig m) =>
+  Sq.Manipulation '[] schemas params '[] ->
+  list x ->
+  m ()
+traversePrepared_ man l = send $ TraversePrepared_ man l (pure ())
+
+-- | See 'Sq.forPrepared_' from @squeal-postgresql@.
+forPrepared_ ::
+  (Sq.ToParams x params, Foldable list, Has (Squeal schemas) sig m) =>
+  list x ->
+  Sq.Manipulation '[] schemas params '[] ->
+  m ()
+forPrepared_ = flip traversePrepared_
+
+newtype SquealPool schemas m k = GetSquealPool (Pool (DBConnection schemas) -> m k)
+
+instance Functor m => Functor (SquealPool schemas m) where
+  fmap f (GetSquealPool mk) = GetSquealPool ((fmap . fmap) f mk)
+  {-# INLINE fmap #-}
+
+instance HFunctor (SquealPool schemas) where
+  hmap f (GetSquealPool mk) = GetSquealPool (fmap f mk)
+  {-# INLINE hmap #-}
+
+instance Effect (SquealPool schemas) where
+  thread ctx f (GetSquealPool mk) = GetSquealPool $ \y -> f (ctx $> mk y)
+  {-# INLINE thread #-}
+
+getSquealPool :: Has (SquealPool schemas) sig m => m (Pool (DBConnection schemas))
+getSquealPool = send $ GetSquealPool pure
