diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,34 @@
+Copyright (c) 2022 Torsten Schmits
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+  disclaimer.
+  2. 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.
+
+Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those
+receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except
+for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell,
+import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired
+or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
+
+  (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of
+  contributors, in source or binary form) alone; or
+  (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such
+  copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to
+  be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
+
+Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this
+license, whether expressly, by implication, estoppel or otherwise.
+
+DISCLAIMER
+
+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 HOLDERS 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/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
diff --git a/incipit-base.cabal b/incipit-base.cabal
new file mode 100644
--- /dev/null
+++ b/incipit-base.cabal
@@ -0,0 +1,128 @@
+cabal-version: 2.2
+
+-- This file has been generated from package.yaml by hpack version 0.34.6.
+--
+-- see: https://github.com/sol/hpack
+
+name:           incipit-base
+version:        0.1.0.0
+synopsis:       A Prelude for Polysemy
+description:    See <https://hackage.haskell.org/package/incipit-base/docs/Incipit-Prelude.html>
+category:       Prelude
+homepage:       https://github.com/tek/incipit#readme
+bug-reports:    https://github.com/tek/incipit/issues
+author:         Torsten Schmits
+maintainer:     haskell@tryp.io
+copyright:      2022 Torsten Schmits
+license:        BSD-2-Clause-Patent
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    readme.md
+    changelog.md
+
+source-repository head
+  type: git
+  location: https://github.com/tek/incipit
+
+library
+  exposed-modules:
+      Incipit.Base
+      Incipit.Debug
+      Incipit.Either
+      Incipit.Libraries
+      Incipit.List
+      Incipit.Misc
+      Incipit.Prelude
+      Incipit.String
+      Incipit.String.Conversion
+      Incipit.String.Reexport
+  reexported-modules:
+      Data.ByteString
+    , Data.ByteString.Builder
+    , Data.ByteString.Lazy
+    , Data.ByteString.Short
+    , Data.IntMap.Lazy
+    , Data.IntMap.Strict
+    , Data.IntSet
+    , Data.Map.Lazy
+    , Data.Map.Strict
+    , Data.Sequence
+    , Data.Set
+    , Data.Text
+    , Data.Text.IO
+    , Data.Text.Lazy
+    , Data.Text.Lazy.IO
+    , Data.Text.Read
+    , Data.Tree
+  hs-source-dirs:
+      lib
+  default-extensions:
+      AllowAmbiguousTypes
+      ApplicativeDo
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      ConstraintKinds
+      DataKinds
+      DefaultSignatures
+      DeriveAnyClass
+      DeriveDataTypeable
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      DisambiguateRecordFields
+      DoAndIfThenElse
+      DuplicateRecordFields
+      EmptyDataDecls
+      ExistentialQuantification
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GADTs
+      GeneralizedNewtypeDeriving
+      InstanceSigs
+      KindSignatures
+      LambdaCase
+      LiberalTypeSynonyms
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NoImplicitPrelude
+      OverloadedStrings
+      OverloadedLists
+      PackageImports
+      PartialTypeSignatures
+      PatternGuards
+      PatternSynonyms
+      PolyKinds
+      QuantifiedConstraints
+      QuasiQuotes
+      RankNTypes
+      RecordWildCards
+      RecursiveDo
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      TypeFamilyDependencies
+      TypeOperators
+      TypeSynonymInstances
+      UndecidableInstances
+      UnicodeSyntax
+      ViewPatterns
+  ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities
+  build-depends:
+      base ==4.*
+    , bytestring
+    , containers
+    , data-default >=0.2
+    , text
+  if impl(ghc >= 8.10)
+    ghc-options: -Wunused-packages
+  default-language: Haskell2010
diff --git a/lib/Incipit/Base.hs b/lib/Incipit/Base.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Base.hs
@@ -0,0 +1,201 @@
+{-# options_haddock ignore-exports #-}
+
+-- |Reexports from @base@.
+module Incipit.Base (
+  module Control.Applicative,
+  module Control.Arrow,
+  module Control.Category,
+  module Control.Monad,
+  module Control.Monad.Fail,
+  module Data.Bifunctor,
+  module Data.Bits,
+  module Data.Bool,
+  module Data.Char,
+  module Data.Coerce,
+  module Data.Either,
+  module Data.Eq,
+  module Data.Foldable,
+  module Data.Function,
+  module Data.Functor,
+  module Data.Functor.Compose,
+  module Data.Functor.Contravariant,
+  module Data.Functor.Identity,
+  module Data.Int,
+  module Data.Kind,
+  module Data.List,
+  module Data.List.NonEmpty,
+  module Data.Maybe,
+  module Data.Monoid,
+  module Data.Ord,
+  module Data.Proxy,
+  module Data.Semigroup,
+  module Data.Traversable,
+  module Data.Tuple,
+  module Data.Typeable,
+  module Data.Void,
+  module Data.Word,
+  module GHC.Base,
+  module GHC.Enum,
+  module GHC.Err,
+  module GHC.Float,
+  module GHC.Generics,
+  module GHC.Num,
+  module GHC.OverloadedLabels,
+  module GHC.Real,
+  module GHC.Show,
+  module GHC.Stack,
+  module GHC.TypeLits,
+  module Numeric.Natural,
+  module System.IO,
+) where
+
+import Control.Applicative (
+  Alternative (..),
+  Applicative (..),
+  Const (..),
+  ZipList (..),
+  liftA2,
+  liftA3,
+  optional,
+  (<**>),
+  )
+import Control.Arrow ((&&&))
+import Control.Category ((<<<), (>>>))
+import Control.Monad (
+  Monad ((>>), (>>=)),
+  MonadPlus (..),
+  filterM,
+  forever,
+  guard,
+  join,
+  mfilter,
+  replicateM,
+  replicateM_,
+  unless,
+  when,
+  zipWithM,
+  zipWithM_,
+  (<$!>),
+  (<=<),
+  (=<<),
+  (>=>),
+  )
+import Control.Monad.Fail (MonadFail (..))
+import Data.Bifunctor (Bifunctor (..))
+import Data.Bits (toIntegralSized, xor)
+import Data.Bool (Bool (..), bool, not, otherwise, (&&), (||))
+import Data.Char (Char, chr)
+import Data.Coerce (Coercible, coerce)
+import Data.Either
+import Data.Eq (Eq (..))
+import Data.Foldable (
+  Foldable (..),
+  all,
+  and,
+  any,
+  asum,
+  concat,
+  concatMap,
+  find,
+  foldlM,
+  forM_,
+  for_,
+  mapM_,
+  or,
+  sequenceA_,
+  sequence_,
+  traverse_,
+  )
+import Data.Function (const, fix, flip, id, on, ($), (&), (.))
+import Data.Functor (Functor (..), void, ($>), (<$>), (<&>))
+import Data.Functor.Compose (Compose (..))
+import Data.Functor.Contravariant (Contravariant (..), (>$<))
+import Data.Functor.Identity (Identity (..))
+import Data.Int (Int, Int16, Int32, Int64, Int8)
+import Data.Kind (Constraint, Type)
+import Data.List (
+  break,
+  drop,
+  dropWhile,
+  filter,
+  genericDrop,
+  genericLength,
+  genericReplicate,
+  genericSplitAt,
+  genericTake,
+  group,
+  inits,
+  intercalate,
+  intersperse,
+  isPrefixOf,
+  iterate,
+  map,
+  permutations,
+  repeat,
+  replicate,
+  reverse,
+  scanl,
+  scanl',
+  scanl1,
+  scanr,
+  scanr1,
+  sort,
+  sortBy,
+  sortOn,
+  span,
+  splitAt,
+  subsequences,
+  tails,
+  take,
+  takeWhile,
+  transpose,
+  uncons,
+  unfoldr,
+  unzip,
+  unzip3,
+  zip,
+  zip3,
+  zipWith,
+  (++),
+  )
+import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
+import Data.Maybe hiding (fromJust)
+import Data.Monoid (Monoid (..))
+import Data.Ord (Down (..), Ord (..), Ordering (..), comparing)
+import Data.Proxy (Proxy (..))
+import Data.Semigroup (Semigroup (..))
+import Data.Traversable (Traversable (..), for, forM, mapAccumL, mapAccumR)
+import Data.Tuple (curry, fst, snd, swap, uncurry)
+import Data.Typeable (Typeable)
+import Data.Void (Void)
+import Data.Word (Word, Word16, Word32, Word64, Word8, byteSwap16, byteSwap32, byteSwap64)
+import GHC.Base (maxInt, minInt, ord, seq, ($!))
+import GHC.Enum (Bounded (..), Enum (..))
+import GHC.Err (error, undefined)
+import GHC.Float (Double (..), Float (..))
+import GHC.Generics (Generic)
+import GHC.Num (Integer, Num (..), subtract)
+import GHC.OverloadedLabels (IsLabel (..))
+import GHC.Real (
+  Fractional (..),
+  Integral (..),
+  Ratio,
+  Rational,
+  Real (..),
+  RealFrac (..),
+  denominator,
+  even,
+  fromIntegral,
+  gcd,
+  lcm,
+  numerator,
+  odd,
+  realToFrac,
+  (^),
+  (^^),
+  )
+import GHC.Show (Show)
+import GHC.Stack (HasCallStack)
+import GHC.TypeLits hiding (ErrorMessage (Text))
+import Numeric.Natural (Natural)
+import System.IO (FilePath, IO, print, putStr, putStrLn)
diff --git a/lib/Incipit/Debug.hs b/lib/Incipit/Debug.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Debug.hs
@@ -0,0 +1,111 @@
+{-# options_haddock prune #-}
+
+-- |Utility functions for trace-printing values prefixed with the current source location.
+module Incipit.Debug where
+
+import qualified Data.Text as Text
+import GHC.Stack (CallStack, SrcLoc (..), callStack, getCallStack)
+import System.IO.Unsafe (unsafePerformIO)
+
+import Incipit.Base (
+  Applicative (pure),
+  Functor ((<$)),
+  HasCallStack,
+  IO,
+  Monad,
+  Semigroup ((<>)),
+  Show,
+  error,
+  fromMaybe,
+  putStrLn,
+  )
+import Incipit.List (last)
+import Incipit.String.Conversion (ToString (toString), ToText (toText), show)
+import Data.Text (Text)
+
+srcLoc :: CallStack -> SrcLoc
+srcLoc = \case
+  (getCallStack -> (_, loc) : _) -> loc
+  _ -> error "Debug.srcLoc: empty CallStack"
+
+debugPrint ::
+  SrcLoc ->
+  Text ->
+  IO ()
+debugPrint SrcLoc {srcLocModule = (toText -> slm), srcLocStartLine} msg =
+  putStrLn (toString moduleName <> ":" <> show srcLocStartLine <> " " <> toString msg)
+  where
+    moduleName =
+      fromMaybe slm (last (Text.splitOn "." slm))
+
+debugPrintWithLoc ::
+  Monad m =>
+  SrcLoc ->
+  Text ->
+  m ()
+debugPrintWithLoc loc msg = do
+  () <- pure (unsafePerformIO (debugPrint loc msg))
+  pure ()
+
+-- |Print a 'Text' in an arbitrary 'Monad'.
+dbg ::
+  HasCallStack =>
+  Monad m =>
+  Text ->
+  m ()
+dbg =
+  debugPrintWithLoc (srcLoc callStack)
+{-# noinline dbg #-}
+
+-- |Print a value with a 'Show' instance in an arbitrary 'Monad'.
+dbgs ::
+  HasCallStack =>
+  Monad m =>
+  Show a =>
+  a ->
+  m ()
+dbgs a =
+  debugPrintWithLoc (srcLoc callStack) (show a)
+{-# noinline dbgs_ #-}
+
+-- |Print a value with a 'Show' instance in an arbitrary 'Monad', returning the value.
+dbgs_ ::
+  HasCallStack =>
+  Monad m =>
+  Show a =>
+  a ->
+  m a
+dbgs_ a =
+  a <$ debugPrintWithLoc (srcLoc callStack) (show a)
+{-# noinline dbgs #-}
+
+-- |Like 'Debug.Trace.trace', but with 'Text' and with source location prefix.
+tr ::
+  HasCallStack =>
+  Text ->
+  a ->
+  a
+tr msg a =
+  unsafePerformIO (a <$ debugPrint (srcLoc callStack) msg)
+{-# noinline tr #-}
+
+-- |Like 'Debug.Trace.traceShowId', but with 'Text' and with source location prefix.
+trs ::
+  Show a =>
+  HasCallStack =>
+  a ->
+  a
+trs a =
+  unsafePerformIO (a <$ debugPrint (srcLoc callStack) (show a))
+{-# noinline trs #-}
+
+-- |Like 'Debug.Trace.traceShow', but with 'Text' and with source location prefix.
+trs' ::
+  Show b =>
+  HasCallStack =>
+  b ->
+  a ->
+  a
+trs' b a =
+  unsafePerformIO (a <$ debugPrint (srcLoc callStack) (show b))
+{-# noinline trs' #-}
diff --git a/lib/Incipit/Either.hs b/lib/Incipit/Either.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Either.hs
@@ -0,0 +1,30 @@
+-- |Some utilities for working with 'Either'.
+module Incipit.Either where
+
+import Incipit.Base
+
+-- |Turn 'Left' into 'Just' and 'Right' into 'Nothing'.
+leftToMaybe :: Either l r -> Maybe l
+leftToMaybe = either Just (const Nothing)
+{-# inline leftToMaybe #-}
+
+-- |Turn 'Right' into 'Just' and 'Left' into 'Nothing'.
+rightToMaybe :: Either l r -> Maybe r
+rightToMaybe = either (const Nothing) Just
+{-# inline rightToMaybe #-}
+
+-- |Turn 'Just' into 'Right' and 'Nothing' into 'Left' with the supplied value.
+maybeToRight :: l -> Maybe r -> Either l r
+maybeToRight l = maybe (Left l) Right
+{-# inline maybeToRight #-}
+
+-- |Turn 'Just' into 'Left' and 'Nothing' into 'Right' with the supplied value.
+maybeToLeft :: r -> Maybe l -> Either l r
+maybeToLeft r = maybe (Right r) Left
+{-# inline maybeToLeft #-}
+
+-- |Extract the value from either side of an 'Either'.
+unify :: Either a a -> a
+unify =
+  either id id
+{-# inline unify #-}
diff --git a/lib/Incipit/Libraries.hs b/lib/Incipit/Libraries.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Libraries.hs
@@ -0,0 +1,19 @@
+{-# options_haddock prune, ignore-exports #-}
+
+-- |Reexports of types from [containers](https://hackage.haskell.org/package/containers) and
+-- [data-default](https://hackage.haskell.org/package/data-default).
+module Incipit.Libraries (
+  module Data.Default,
+  module Data.IntMap.Strict,
+  module Data.IntSet,
+  module Data.Map.Strict,
+  module Data.Sequence,
+  module Data.Set,
+) where
+
+import Data.Default (Default (..))
+import Data.IntMap.Strict (IntMap)
+import Data.IntSet (IntSet)
+import Data.Map.Strict (Map)
+import Data.Sequence (Seq)
+import Data.Set (Set)
diff --git a/lib/Incipit/List.hs b/lib/Incipit/List.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/List.hs
@@ -0,0 +1,33 @@
+-- |Variants of the uncons and unsnoc combinators for lists that return 'Maybe'.
+module Incipit.List where
+
+import qualified Data.List as List
+import Prelude (Maybe (..))
+
+-- |Return 'Just' the head of a list.
+head :: [a] -> Maybe a
+head = \case
+  [] -> Nothing
+  a : _ -> Just a
+{-# inline head #-}
+
+-- |Return 'Just' the last element of a list.
+last :: [a] -> Maybe a
+last = \case
+  [] -> Nothing
+  as -> Just (List.last as)
+{-# inline last #-}
+
+-- |Return 'Just' the non-last elements of a list.
+init :: [a] -> Maybe [a]
+init = \case
+  [] -> Nothing
+  as -> Just (List.init as)
+{-# inline init #-}
+
+-- |Return 'Just' the non-head elements of a list.
+tail :: [a] -> Maybe [a]
+tail = \case
+  [] -> Nothing
+  as -> Just (List.tail as)
+{-# inline tail #-}
diff --git a/lib/Incipit/Misc.hs b/lib/Incipit/Misc.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Misc.hs
@@ -0,0 +1,43 @@
+-- |Misc combinators.
+module Incipit.Misc where
+
+import Incipit.Base
+
+-- |Convenience alias for @pure ()@.
+unit ::
+  Applicative f =>
+  f ()
+unit =
+  pure ()
+{-# inline unit #-}
+
+-- |Variant of 'when' that takes a monadic action for the condition.
+whenM ::
+  Monad m =>
+  m Bool ->
+  m () ->
+  m ()
+whenM condM action =
+  condM >>= \ cond -> when cond action
+{-# inline whenM #-}
+
+-- |Variant of 'unless' that takes a monadic action for the condition.
+unlessM ::
+  Monad m =>
+  m Bool ->
+  m () ->
+  m ()
+unlessM condM action =
+  condM >>= \ cond -> unless cond action
+{-# inline unlessM #-}
+
+-- |Variant of @if@/@then@/@else@ that takes a monadic action for the condition and branches.
+ifM ::
+  Monad m =>
+  m Bool ->
+  m a ->
+  m a ->
+  m a
+ifM condM onTrue onFalse =
+  condM >>= \ cond -> if cond then onTrue else onFalse
+{-# inline ifM #-}
diff --git a/lib/Incipit/Prelude.hs b/lib/Incipit/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/Prelude.hs
@@ -0,0 +1,25 @@
+{-# options_haddock prune #-}
+
+-- |This module is intended to be used by the packages [incipit-core](https://hackage.haskell.org/package/incipit-core)
+-- and [incipit](https://hackage.haskell.org/package/incipit), which are Preludes for packages using
+-- [Polysemy](https://hackage.haskell.org/package/polysemy).
+--
+-- Aside from a few additional names from @base@, this module also exports the string system from
+-- [relude](https://hackage.haskell.org/package/relude), some tracing functions, and a small set of other combinators.
+module Incipit.Prelude (
+  module Incipit.Base,
+  module Incipit.Debug,
+  module Incipit.Either,
+  module Incipit.Libraries,
+  module Incipit.List,
+  module Incipit.Misc,
+  module Incipit.String,
+) where
+
+import Incipit.Base
+import Incipit.Debug
+import Incipit.Either
+import Incipit.Libraries
+import Incipit.List
+import Incipit.Misc
+import Incipit.String
diff --git a/lib/Incipit/String.hs b/lib/Incipit/String.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/String.hs
@@ -0,0 +1,57 @@
+{- |
+Copyright:  (c) 2016 Stephen Diehl
+            (c) 2016-2018 Serokell
+            (c) 2018-2021 Kowainik
+SPDX-License-Identifier: MIT
+Maintainer:  Kowainik <xrom.xkov@gmail.com>
+Stability:   Stable
+Portability: Portable
+
+Type classes for conversion between different string representations.
+
+The table below represents the @relude@ concept of conversion between the following types:
+
+* 'Text'
+* 'String'
+* 'ByteString'
+* 'LText'
+* 'LByteString'
+* 'ShortByteString'
+
+
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+|     From \\ To        |       'Text'        |   'String'   | 'ByteString' |      'LText'       |   'LByteString'        |   'ShortByteString'      |
++=======================+=====================+==============+==============+====================+========================+==========================+
+| __'Text'__            | ~                   | 'toString'   | 'encodeUtf8' | 'toLazy'/'toLText' | 'encodeUtf8'           | 'encodeUtf8'             |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+| __'String'__          | 'toText'            | ~            | 'encodeUtf8' | 'toLText'          | 'encodeUtf8'           | 'encodeUtf8'             |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+| __'ByteString'__      | 'decodeUtf8'        | 'decodeUtf8' | ~            | 'decodeUtf8'       | 'toLazy'               | 'toShort'                |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+| __'LText'__           | 'toStrict'/'toText' | 'toString'   | 'encodeUtf8' |  ~                 | 'encodeUtf8'           | 'encodeUtf8'             |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+| __'LByteString'__     | 'decodeUtf8'        | 'decodeUtf8' | 'toStrict'   | 'decodeUtf8'       | ~                      | @'toShort' . 'toStrict'@ |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+| __'ShortByteString'__ | 'decodeUtf8'        | 'decodeUtf8' | 'fromShort'  | 'decodeUtf8'       |@'toLazy' . 'fromShort'@| ~                        |
++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------+
+
+-}
+
+module Incipit.String
+    ( module Incipit.String.Reexport
+      -- $reexport
+    , module Incipit.String.Conversion
+      -- $conversion
+    ) where
+
+import Incipit.String.Conversion
+import Incipit.String.Reexport
+
+{- $reexport
+Reexport data types and functions to work with 'Text', 'ByteString',
+'ShortByteString'.
+-}
+{- $conversion
+Conversion functions between 'Text', 'String', 'ByteString'.
+Also some read|show helper functions.
+-}
diff --git a/lib/Incipit/String/Conversion.hs b/lib/Incipit/String/Conversion.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/String/Conversion.hs
@@ -0,0 +1,553 @@
+{-# options_haddock prune #-}
+
+{- |
+Copyright:  (c) 2016 Stephen Diehl
+            (c) 2016-2018 Serokell
+            (c) 2018-2021 Kowainik
+SPDX-License-Identifier: MIT
+Maintainer:  Kowainik <xrom.xkov@gmail.com>
+Stability:   Stable
+Portability: Portable
+
+This module implements type class which allow to have conversion to and from
+'Incipit.String.Reexport.Text', 'String' and 'ByteString' types
+(including both strict and lazy versions). Usually you need to export
+'Incipit.String.Reexport.Text' modules qualified and use 'T.pack' \/ 'T.unpack'
+functions to convert to\/from 'Incipit.String.Reexport.Text'. Now you can just
+use 'toText' \/ 'toString' functions.
+-}
+
+module Incipit.String.Conversion
+    ( -- * Convenient type aliases
+      LText
+    , LByteString
+
+      -- * Conversion type classes
+    , ConvertUtf8 (..)
+    , ToText (..)
+    , ToLText (..)
+    , ToString (..)
+    , LazyStrict (..)
+    , fromLazy
+    , fromStrict
+
+      -- * Show and read functions
+    , readEither
+    , show
+    ) where
+
+import qualified Data.ByteString.Lazy as LB
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Text.Encoding.Error as T
+import qualified Data.Text.Lazy as LT
+import qualified Data.Text.Lazy.Encoding as LT
+import qualified GHC.Show as Show (Show (show))
+import qualified GHC.TypeLits as Error
+import qualified Text.Read (readEither)
+
+import Incipit.Base
+import Incipit.String.Reexport (
+  ByteString,
+  IsString,
+  Read,
+  ShortByteString,
+  String,
+  Text,
+  fromShort,
+  fromString,
+  toShort,
+  )
+
+
+-- $setup
+-- >>> import Relude
+
+-- | Type synonym for 'Data.Text.Lazy.Text'.
+type LText = LT.Text
+
+-- | Type synonym for 'Data.ByteString.Lazy.ByteString'.
+type LByteString = LB.ByteString
+
+
+-- | Type class for conversion to utf8 representation of text.
+class ConvertUtf8 a b where
+    -- | Encode as utf8 string (usually 'B.ByteString').
+    --
+    -- >>> encodeUtf8 @Text @ByteString "патак"
+    -- "\208\191\208\176\209\130\208\176\208\186"
+    encodeUtf8 :: a -> b
+
+    -- | Decode from utf8 string.
+    --
+    -- >>> decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"
+    -- "\1087\1072\1090\1072\1082"
+    -- >>> putTextLn $ decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"
+    -- патак
+    decodeUtf8 :: b -> a
+
+    {- | Decode as utf8 string but returning execption if byte sequence is malformed.
+
+#if MIN_VERSION_text(1,2,3)
+    >>> decodeUtf8 @Text @ByteString "\208\208\176\209\130\208\176\208\186"
+    "\65533\1072\1090\1072\1082"
+#else
+    >>> decodeUtf8 @Text @ByteString "\208\208\176\209\130\208\176\208\186"
+    "\65533\65533\1090\1072\1082"
+#endif
+    >>> decodeUtf8Strict @Text @ByteString "\208\208\176\209\130\208\176\208\186"
+    Left Cannot decode byte '\xd0': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream
+    -}
+    decodeUtf8Strict :: b -> Either T.UnicodeException a
+
+instance ConvertUtf8 String ByteString where
+    encodeUtf8 :: String -> ByteString
+    encodeUtf8 = T.encodeUtf8 . T.pack
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ByteString -> String
+    decodeUtf8 = T.unpack . T.decodeUtf8
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ByteString -> Either T.UnicodeException String
+    decodeUtf8Strict = (T.unpack <$>) . decodeUtf8Strict
+    {-# INLINE decodeUtf8Strict #-}
+
+instance ConvertUtf8 Text ByteString where
+    encodeUtf8 :: Text -> ByteString
+    encodeUtf8 = T.encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ByteString -> Text
+    decodeUtf8 = T.decodeUtf8With T.lenientDecode
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ByteString -> Either T.UnicodeException Text
+    decodeUtf8Strict = T.decodeUtf8'
+    {-# INLINE decodeUtf8Strict #-}
+
+instance ConvertUtf8 LText ByteString where
+    encodeUtf8 :: LText -> ByteString
+    encodeUtf8 = LB.toStrict . encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ByteString -> LText
+    decodeUtf8 = LT.decodeUtf8With T.lenientDecode . LB.fromStrict
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ByteString -> Either T.UnicodeException LText
+    decodeUtf8Strict = decodeUtf8Strict . LB.fromStrict
+    {-# INLINE decodeUtf8Strict #-}
+
+{- | Converting 'String' to 'LB.ByteString' might be a slow operation.
+Consider using lazy bytestring at first place.
+-}
+instance ConvertUtf8 String LByteString where
+    encodeUtf8 :: String -> LByteString
+    encodeUtf8 = LT.encodeUtf8 . LT.pack
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: LByteString -> String
+    decodeUtf8 = LT.unpack . LT.decodeUtf8
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: LByteString -> Either T.UnicodeException String
+    decodeUtf8Strict = (T.unpack <$>) . decodeUtf8Strict
+    {-# INLINE decodeUtf8Strict #-}
+
+instance ConvertUtf8 Text LByteString where
+    encodeUtf8 :: Text -> LByteString
+    encodeUtf8 = LB.fromStrict . T.encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: LByteString -> Text
+    decodeUtf8 = T.decodeUtf8With T.lenientDecode . LB.toStrict
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: LByteString -> Either T.UnicodeException Text
+    decodeUtf8Strict = T.decodeUtf8' . LB.toStrict
+    {-# INLINE decodeUtf8Strict #-}
+
+instance ConvertUtf8 LText LByteString where
+    encodeUtf8 :: LText -> LByteString
+    encodeUtf8 = LT.encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: LByteString -> LText
+    decodeUtf8 = LT.decodeUtf8With T.lenientDecode
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: LByteString -> Either T.UnicodeException LText
+    decodeUtf8Strict = LT.decodeUtf8'
+    {-# INLINE decodeUtf8Strict #-}
+
+-- | @since 0.6.0.0
+instance ConvertUtf8 String ShortByteString where
+    encodeUtf8 :: String -> ShortByteString
+    encodeUtf8 = toShort . encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ShortByteString -> String
+    decodeUtf8 = decodeUtf8 . fromShort
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException String
+    decodeUtf8Strict = decodeUtf8Strict . fromShort
+    {-# INLINE decodeUtf8Strict #-}
+
+-- | @since 0.6.0.0
+instance ConvertUtf8 Text ShortByteString where
+    encodeUtf8 :: Text -> ShortByteString
+    encodeUtf8 = toShort . encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ShortByteString -> Text
+    decodeUtf8 = decodeUtf8 . fromShort
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException Text
+    decodeUtf8Strict = decodeUtf8Strict . fromShort
+    {-# INLINE decodeUtf8Strict #-}
+
+-- | @since 0.6.0.0
+instance ConvertUtf8 LText ShortByteString where
+    encodeUtf8 :: LText -> ShortByteString
+    encodeUtf8 = toShort . encodeUtf8
+    {-# INLINE encodeUtf8 #-}
+
+    decodeUtf8 :: ShortByteString -> LText
+    decodeUtf8 = decodeUtf8 . fromShort
+    {-# INLINE decodeUtf8 #-}
+
+    decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException LText
+    decodeUtf8Strict = decodeUtf8Strict . fromShort
+    {-# INLINE decodeUtf8Strict #-}
+
+-- | Type class for converting other strings to 'T.Text'.
+class ToText a where
+    toText :: a -> Text
+
+instance ToText String where
+    toText :: String -> Text
+    toText = T.pack
+    {-# INLINE toText #-}
+
+instance ToText Text where
+    toText :: Text -> Text
+    toText = id
+    {-# INLINE toText #-}
+
+instance ToText LText where
+    toText :: LText -> Text
+    toText = LT.toStrict
+    {-# INLINE toText #-}
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toText ("some string" :: ByteString)
+...
+... Type 'ByteString' doesn't have instance of 'ToText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ByteString -> Text
+          decodeUtf8Strict :: ByteString -> Either UnicodeException Text
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToText "ByteString" "Text" => ToText ByteString where
+    toText :: ByteString -> Text
+    toText = error "Unreachable ByteString instance of ToText"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toText ("some string" :: LByteString)
+...
+... Type 'LByteString' doesn't have instance of 'ToText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: LByteString -> Text
+          decodeUtf8Strict :: LByteString -> Either UnicodeException Text
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToText "LByteString" "Text" => ToText LByteString where
+    toText :: LByteString -> Text
+    toText = error "Unreachable LByteString instance of ToText"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toText ("some string" :: ShortByteString)
+...
+... Type 'ShortByteString' doesn't have instance of 'ToText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ShortByteString -> Text
+          decodeUtf8Strict :: ShortByteString -> Either UnicodeException Text
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToText "ShortByteString" "Text" => ToText ShortByteString where
+    toText :: ShortByteString -> Text
+    toText = error "Unreachable ShortByteString instance of ToText"
+
+-- | Type class for converting other strings to 'LT.Text'.
+class ToLText a where
+    toLText :: a -> LText
+
+instance ToLText String where
+    toLText :: String -> LText
+    toLText = LT.pack
+    {-# INLINE toLText #-}
+
+instance ToLText Text where
+    toLText :: Text -> LText
+    toLText = LT.fromStrict
+    {-# INLINE toLText #-}
+
+instance ToLText LT.Text where
+    toLText :: LText -> LText
+    toLText = id
+    {-# INLINE toLText #-}
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toLText ("some string" :: ByteString)
+...
+... Type 'ByteString' doesn't have instance of 'ToLText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ByteString -> LText
+          decodeUtf8Strict :: ByteString -> Either UnicodeException LText
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToLText "ByteString" "LText" => ToLText ByteString where
+    toLText :: ByteString -> LText
+    toLText = error "Unreachable ByteString instance of ToLText"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toLText ("some string" :: LByteString)
+...
+... Type 'LByteString' doesn't have instance of 'ToLText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: LByteString -> LText
+          decodeUtf8Strict :: LByteString -> Either UnicodeException LText
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToLText "LByteString" "LText" => ToLText LByteString where
+    toLText :: LByteString -> LText
+    toLText = error "Unreachable LByteString instance of ToLText"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toLText ("some string" :: ShortByteString)
+...
+... Type 'ShortByteString' doesn't have instance of 'ToLText'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ShortByteString -> LText
+          decodeUtf8Strict :: ShortByteString -> Either UnicodeException LText
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToLText "ShortByteString" "LText" => ToLText ShortByteString where
+    toLText :: ShortByteString -> LText
+    toLText = error "Unreachable ShortByteString instance of ToLText"
+
+-- | Type class for converting other strings to 'String'.
+class ToString a where
+    toString :: a -> String
+
+instance ToString String where
+    toString :: String -> String
+    toString = id
+    {-# INLINE toString #-}
+
+instance ToString Text where
+    toString :: Text -> String
+    toString = T.unpack
+    {-# INLINE toString #-}
+
+instance ToString LText where
+    toString :: LText -> String
+    toString = LT.unpack
+    {-# INLINE toString #-}
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toString ("some string" :: ByteString)
+...
+... Type 'ByteString' doesn't have instance of 'ToString'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ByteString -> String
+          decodeUtf8Strict :: ByteString -> Either UnicodeException String
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToString "ByteString" "String" => ToString ByteString where
+    toString :: ByteString -> String
+    toString = error "Unreachable ByteString instance of ToString"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toString ("some string" :: LByteString)
+...
+... Type 'LByteString' doesn't have instance of 'ToString'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: LByteString -> String
+          decodeUtf8Strict :: LByteString -> Either UnicodeException String
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToString "LByteString" "String" => ToString LByteString where
+    toString :: LByteString -> String
+    toString = error "Unreachable LByteString instance of ToString"
+
+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.
+
+You should always specify encoding of bytes explicitly.
+
+In case it is used by mistake, the user will see the following:
+
+>>> toString ("some string" :: ShortByteString)
+...
+... Type 'ShortByteString' doesn't have instance of 'ToString'.
+      Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:
+          decodeUtf8       :: ShortByteString -> String
+          decodeUtf8Strict :: ShortByteString -> Either UnicodeException String
+...
+
+@since 0.6.0.0
+-}
+instance EncodingError ToString "ShortByteString" "String" => ToString ShortByteString where
+    toString :: ShortByteString -> String
+    toString = error "Unreachable ShortByteString instance of ToString"
+
+-- | Helper type family to produce error messages
+type family EncodingError
+    (c :: Type -> Constraint)
+    (from :: Symbol)
+    (to :: Symbol)
+    :: Constraint
+  where
+    EncodingError c from to = TypeError
+        ( 'Error.Text "Type '" ':<>: 'Error.Text from ':<>: 'Error.Text "' doesn't have instance of '"
+        ':<>: 'ShowType c ':<>: 'Error.Text "'."
+        ':$$: 'Error.Text "Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:"
+        ':$$: 'Error.Text "    decodeUtf8       :: " ':<>: 'Error.Text from
+        ':<>: 'Error.Text " -> " ':<>: 'Error.Text to
+        ':$$: 'Error.Text "    decodeUtf8Strict :: " ':<>: 'Error.Text from
+        ':<>: 'Error.Text " -> Either UnicodeException " ':<>: 'Error.Text to
+        )
+
+{- | Version of 'Text.Read.readEither' that returns 'Text' in case of the parse
+error.
+
+>>> readEither @Int "123"
+Right 123
+>>> readEither @Int "aa"
+Left "Prelude.read: no parse"
+-}
+readEither :: (Read a) => String -> Either Text a
+readEither = first toText . Text.Read.readEither
+{-# INLINEABLE readEither #-}
+
+{- | Generalized version of 'Prelude.show'. Unlike 'Prelude.show' this function
+is polymorphic in its result type. This makes it more convenient to work with
+data types like 'Incipit.String.Reexport.Text' or 'ByteString'. However, if you
+pass the result of 'show' to a function that expects polymorphic argument, this
+can break type inference, so use @-XTypeApplications@ to specify the textual
+type explicitly.
+
+>>> show (42 :: Int)
+"42"
+>>> show (42 :: Double)
+"42.0"
+>>> print (show @Text True)
+"True"
+-}
+show :: ∀ b a . (Show.Show a, IsString b) => a -> b
+show x = fromString (Show.show x)
+{-# INLINE show #-}
+{-# SPECIALIZE show :: Show.Show  a => a -> Text  #-}
+{-# SPECIALIZE show :: Show.Show  a => a -> LText  #-}
+{-# SPECIALIZE show :: Show.Show  a => a -> ByteString  #-}
+{-# SPECIALIZE show :: Show.Show  a => a -> LByteString  #-}
+{-# SPECIALIZE show :: Show.Show  a => a -> String  #-}
+
+{- | Type class for lazy-strict conversions.
+
+@since 0.1.0
+-}
+class LazyStrict l s | l -> s, s -> l where
+    toLazy   :: s -> l
+    toStrict :: l -> s
+
+-- | Alias for 'toStrict' function.
+fromLazy :: LazyStrict l s => l -> s
+fromLazy = toStrict
+{-# INLINE fromLazy #-}
+{-# SPECIALIZE fromLazy :: LByteString -> ByteString  #-}
+{-# SPECIALIZE fromLazy :: LText -> Text  #-}
+
+-- | Alias for 'toLazy' function.
+fromStrict :: LazyStrict l s => s -> l
+fromStrict = toLazy
+{-# INLINE fromStrict #-}
+{-# SPECIALIZE fromStrict :: ByteString -> LByteString  #-}
+{-# SPECIALIZE fromStrict :: Text -> LText  #-}
+
+instance LazyStrict LByteString ByteString where
+    toLazy :: ByteString -> LByteString
+    toLazy = LB.fromStrict
+    {-# INLINE toLazy #-}
+
+    toStrict :: LByteString -> ByteString
+    toStrict = LB.toStrict
+    {-# INLINE toStrict #-}
+
+instance LazyStrict LText Text where
+    toLazy :: Text -> LText
+    toLazy = LT.fromStrict
+    {-# INLINE toLazy #-}
+
+    toStrict :: LText -> Text
+    toStrict = LT.toStrict
+    {-# INLINE toStrict #-}
diff --git a/lib/Incipit/String/Reexport.hs b/lib/Incipit/String/Reexport.hs
new file mode 100644
--- /dev/null
+++ b/lib/Incipit/String/Reexport.hs
@@ -0,0 +1,209 @@
+{-# options_haddock prune #-}
+
+{- |
+Copyright:  (c) 2016 Stephen Diehl
+            (c) 2016-2018 Serokell
+            (c) 2018-2021 Kowainik
+SPDX-License-Identifier: MIT
+Maintainer:  Kowainik <xrom.xkov@gmail.com>
+Stability:   Stable
+Portability: Portable
+
+Reexports functions to work with 'Data.Text.Text', 'ByteString' and
+'ShortByteString' types.
+-}
+
+module Incipit.String.Reexport
+    ( -- * String
+      module Data.String
+    , module Text.Read
+
+      -- * Text
+    , Text
+    , lines
+    , unlines
+    , words
+    , unwords
+    , module Data.Text.Encoding
+    , module Data.Text.Encoding.Error
+
+      -- * ByteString
+    , ByteString
+
+      -- * ShortByteString
+    , ShortByteString
+    , toShort
+    , fromShort
+    ) where
+
+import Data.ByteString (ByteString)
+import Data.ByteString.Short (ShortByteString, fromShort, toShort)
+import Data.String (IsString (..), String)
+import qualified Data.Text as Text
+import Data.Text (Text)
+import Data.Text.Encoding (decodeUtf8', decodeUtf8With)
+import Data.Text.Encoding.Error (OnDecodeError, OnError, UnicodeException, lenientDecode, strictDecode)
+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)
+import Text.Read (Read, readMaybe, reads)
+
+import Incipit.Base (Constraint, Type)
+
+
+-- $setup
+-- >>> import Relude
+
+-- | For tracking usage of Text instead of String
+type IsText
+    (t :: Type)      -- Textual type, e.g. Text, String
+    (fun :: Symbol)  -- Function name
+    = (t ~ Text, CheckText t fun)
+
+type family CheckText (t :: Type) (fun :: Symbol) :: Constraint where
+    CheckText Text _ = ()
+    CheckText String fun = TypeError
+        ( 'Text "'" ':<>: 'Text fun ':<>: 'Text "' works with 'Text', not 'String'."
+        ':$$: 'Text "Possible fixes:"
+        ':$$: 'Text "    1. Make sure OverloadedStrings extension is enabled."
+        ':$$: 'Text "    2. Apply 'toText' to a single value."
+        ':$$: 'Text "    3. Apply 'map toText' to the list value."
+        )
+    CheckText a fun = TypeError
+        ( 'Text "'" ':<>: 'Text fun ':<>: 'Text "' works with 'Text'"
+        ':$$: 'Text "But given: '" ':<>: 'ShowType a ':<>: 'Text "'"
+        )
+
+{- | 'lines' takes 'Data.Text.Text' and splits it into the list by lines.
+
+Actual type of this function is the following:
+
+@
+lines :: 'Data.Text.Text' -> ['Data.Text.Text']
+@
+
+but it was given a more complex type to provide friendlier compile time errors.
+
+>>> lines ""
+[]
+>>> lines "one line"
+["one line"]
+>>> lines "line 1\nline 2"
+["line 1","line 2"]
+>>> lines ("string line" :: String)
+...
+... 'lines' works with 'Text', not 'String'.
+      Possible fixes:
+          1. Make sure OverloadedStrings extension is enabled.
+          2. Apply 'toText' to a single value.
+          3. Apply 'map toText' to the list value.
+...
+>>> lines True
+...
+... 'lines' works with 'Text'
+      But given: 'Bool'
+...
+-}
+lines :: IsText t "lines" => t -> [t]
+lines = Text.lines
+{-# INLINE lines #-}
+
+{- | 'unlines' takes list of 'Data.Text.Text' values and joins them with line separator.
+
+Actual type of this function is the following:
+
+@
+unlines :: ['Data.Text.Text'] -> 'Data.Text.Text'
+@
+
+but it was given a more complex type to provide friendlier compile time errors.
+
+>>> unlines []
+""
+>>> unlines ["line 1"]
+"line 1\n"
+>>> unlines ["first line", "second line"]
+"first line\nsecond line\n"
+>>> unlines (["line 1", "line 2"] :: [String])
+...
+... 'unlines' works with 'Text', not 'String'.
+      Possible fixes:
+          1. Make sure OverloadedStrings extension is enabled.
+          2. Apply 'toText' to a single value.
+          3. Apply 'map toText' to the list value.
+...
+>>> unlines [True, False]
+...
+... 'unlines' works with 'Text'
+      But given: 'Bool'
+...
+-}
+unlines :: IsText t "unlines" => [t] -> t
+unlines = Text.unlines
+{-# INLINE unlines #-}
+
+{- | 'words' takes 'Data.Text.Text' and splits it into the list by words.
+
+Actual type of this function is the following:
+
+@
+words :: 'Data.Text.Text' -> ['Data.Text.Text']
+@
+
+but it was given a more complex type to provide friendlier compile time errors.
+
+>>> words ""
+[]
+>>> words "one line"
+["one","line"]
+>>> words "   >_<   "
+[">_<"]
+>>> words ("string words" :: String)
+...
+... 'words' works with 'Text', not 'String'.
+      Possible fixes:
+          1. Make sure OverloadedStrings extension is enabled.
+          2. Apply 'toText' to a single value.
+          3. Apply 'map toText' to the list value.
+...
+>>> words True
+...
+... 'words' works with 'Text'
+      But given: 'Bool'
+...
+-}
+words :: IsText t "words" => t -> [t]
+words = Text.words
+{-# INLINE words #-}
+
+{- | 'unwords' takes list of 'Data.Text.Text' values and joins them with space character.
+
+Actual type of this function is the following:
+
+@
+unwords :: ['Data.Text.Text'] -> 'Data.Text.Text'
+@
+
+but it was given a more complex type to provide friendlier compile time errors.
+
+>>> unwords []
+""
+>>> unwords ["singleWord"]
+"singleWord"
+>>> unwords ["word", "another"]
+"word another"
+>>> unwords (["word", "another"] :: [String])
+...
+... 'unwords' works with 'Text', not 'String'.
+      Possible fixes:
+          1. Make sure OverloadedStrings extension is enabled.
+          2. Apply 'toText' to a single value.
+          3. Apply 'map toText' to the list value.
+...
+>>> unwords [True, False]
+...
+... 'unwords' works with 'Text'
+      But given: 'Bool'
+...
+-}
+unwords :: IsText t "unwords" => [t] -> t
+unwords = Text.unwords
+{-# INLINE unwords #-}
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,5 @@
+This library is part of [incipit], a set of `Prelude` modules for the [Polysemy] ecosystem.
+It contains the reexports from `base` that are inherited by the other two packages.
+
+[incipit]: https://hackage.haskell.org/package/incipit
+[Polysemy]: https://hackage.haskell.org/package/polysemy
