diff --git a/bricks-internal.cabal b/bricks-internal.cabal
new file mode 100644
--- /dev/null
+++ b/bricks-internal.cabal
@@ -0,0 +1,59 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 0ab2b6bce3c64bf68bd6f56a735318f614ddcd80c2bc0ca7a0422d985e01cb38
+
+name:           bricks-internal
+version:        0.0.0.4
+synopsis:       ...
+
+description:    ...
+category:       Language
+homepage:       https://github.com/chris-martin/bricks#readme
+bug-reports:    https://github.com/chris-martin/bricks/issues
+author:         Chris Martin <ch.martin@gmail.com>
+maintainer:     Chris Martin <ch.martin@gmail.com>
+license:        Apache-2.0
+license-file:   license.txt
+build-type:     Simple
+cabal-version:  >= 1.10
+
+source-repository head
+  type: git
+  location: https://github.com/chris-martin/bricks
+
+library
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >=4.9 && <4.11
+    , containers >=0.5.7 && <0.6
+    , either-list-functions ==0.0.0.2
+    , text >=1.2.2 && <1.3
+  exposed-modules:
+      Bricks.Internal.List
+      Bricks.Internal.Map
+      Bricks.Internal.Prelude
+      Bricks.Internal.Seq
+      Bricks.Internal.Text
+  other-modules:
+      Paths_bricks_internal
+  default-language: Haskell2010
+
+test-suite doctest
+  type: exitcode-stdio-1.0
+  main-is: doctest.hs
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded
+  build-depends:
+      base >=4.9 && <4.11
+    , containers >=0.5.7 && <0.6
+    , doctest >=0.11 && <0.14
+    , either-list-functions ==0.0.0.2
+    , text >=1.2.2 && <1.3
+  other-modules:
+      Paths_bricks_internal
+  default-language: Haskell2010
diff --git a/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,13 @@
+Copyright 2017 Chris Martin
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/src/Bricks/Internal/List.hs b/src/Bricks/Internal/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Bricks/Internal/List.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Bricks.Internal.List
+
+  ( module Data.List
+  , module Data.List.EitherFunctions
+
+  , minimum, maximum
+
+  ) where
+
+-- either-list-functions
+import Data.List.EitherFunctions
+
+-- base
+import           Data.List  hiding (maximum, minimum)
+import qualified Data.List  as List
+import           Data.Maybe (Maybe (..))
+import           Data.Ord   (Ord (..))
+
+{- |
+
+>>> minimum [1,2,3]
+Just 1
+
+>>> minimum []
+Nothing
+
+-}
+minimum :: Ord a => [a] -> Maybe a
+minimum =
+  \case
+    [] -> Nothing
+    xs -> Just (List.minimum xs)
+
+{- |
+
+>>> maximum [1,2,3]
+Just 3
+
+>>> maximum []
+Nothing
+
+-}
+maximum :: Ord a => [a] -> Maybe a
+maximum =
+  \case
+    [] -> Nothing
+    xs -> Just (List.maximum xs)
diff --git a/src/Bricks/Internal/Map.hs b/src/Bricks/Internal/Map.hs
new file mode 100644
--- /dev/null
+++ b/src/Bricks/Internal/Map.hs
@@ -0,0 +1,48 @@
+module Bricks.Internal.Map
+  ( module Data.Map
+
+  , exactKeys
+  , restrictKeys
+
+  ) where
+
+-- Containers
+import           Data.Map hiding (restrictKeys)
+import           Data.Set (Set)
+import qualified Data.Set as Set
+
+{- | This function was added in containers version 0.5.8 which we're not using
+yet. -}
+
+restrictKeys :: Ord k => Map k a -> Set k -> Map k a
+m `restrictKeys` s = filterWithKey (\k _ -> k `Set.member` s) m
+
+{- | If @s@ is a subset of the keys in @m@ then
+
+> m exactKeys s = Right (m `restrictKeys` s)
+
+Otherwise, @m exactKeys s = Left s'@ where @s'@ is the keys that are missing
+from @m@. -}
+
+-- | ==== Examples
+--
+-- >>> :{
+-- >>> fromList [('a', 1), ('b', 2), ('c', 3)]
+-- >>>   `exactKeys` Set.fromList ['a', 'b']
+-- >>> :}
+-- Right (fromList [('a',1),('b',2)])
+--
+-- >>> :{
+-- >>> fromList [('a', 1), ('b', 2), ('c', 3)]
+-- >>>   `exactKeys` Set.fromList ['a', 'x', 'y']
+-- >>> :}
+-- Left (fromList "xy")
+
+exactKeys :: Ord k => Map k a -> Set k -> Either (Set k) (Map k a)
+m `exactKeys` s =
+  let
+    s' = s `Set.difference` keysSet m
+  in
+    if Set.null s'
+    then Right (m `restrictKeys` s)
+    else Left s'
diff --git a/src/Bricks/Internal/Prelude.hs b/src/Bricks/Internal/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/src/Bricks/Internal/Prelude.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Bricks.Internal.Prelude
+
+  ( (<&>)
+
+  , module Control.Applicative
+  , module Control.Arrow
+  , module Control.Monad
+  , module Data.Bool
+  , module Data.Char
+  , module Data.Either
+  , module Data.Eq
+  , module Data.Foldable
+  , module Data.Function
+  , module Data.Functor
+  , module Data.Maybe
+  , module Data.Monoid
+  , module Data.Ord
+  , module Data.Semigroup
+  , module Data.String
+  , module Data.Traversable
+  , module Data.Tuple
+  , module GHC.Stack
+  , module Numeric.Natural
+  , module Prelude
+  , module Text.Show
+
+  ) where
+
+import Control.Applicative (Applicative, pure, (*>), (<*), (<*>), (<|>))
+import Control.Arrow       ((>>>))
+import Control.Monad       (Monad, (<=<), (=<<), (>=>), (>>=))
+import Data.Bool           (Bool (False, True), not, (&&), (||))
+import Data.Char           (Char)
+import Data.Either         (Either (..), either)
+import Data.Eq             (Eq ((/=), (==)))
+import Data.Foldable       (asum, fold, foldMap, foldl, foldl1, foldr, foldr1,
+                            for_, traverse_)
+import Data.Function       (const, flip, id, ($), (&), (.))
+import Data.Functor        (Functor, fmap, void, ($>), (<$), (<$>))
+import Data.Maybe          (Maybe (Just, Nothing), catMaybes, isJust, isNothing,
+                            maybe)
+import Data.Monoid         (Monoid (mappend, mempty))
+import Data.Ord            (Ord (..))
+import Data.Semigroup      (Semigroup ((<>)))
+import Data.String         (String)
+import Data.Traversable    (traverse)
+import Data.Tuple          (fst, snd)
+import GHC.Stack           (HasCallStack)
+import Numeric.Natural     (Natural)
+import Prelude             (undefined)
+import Text.Show           (Show (show, showList, showsPrec), shows)
+
+(<&>) :: Functor f => f a -> (a -> b) -> f b
+(<&>) = flip fmap
+infixl 1 <&>
diff --git a/src/Bricks/Internal/Seq.hs b/src/Bricks/Internal/Seq.hs
new file mode 100644
--- /dev/null
+++ b/src/Bricks/Internal/Seq.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Bricks.Internal.Seq
+
+  ( Seq
+
+  , (<|), (|>)
+
+  , adjust
+  , adjustLast
+  , append
+  , concat
+  , concatMap
+  , dropWhileL
+  , dropWhileR
+  , empty
+  , filter
+  , fromList
+  , intersperse
+  , length
+  , map
+  , minimum
+  , maximum
+  , null
+  , toList
+  , trimWhile
+  , singleton
+
+  ) where
+
+-- Containers
+import Data.Sequence (Seq, adjust, dropWhileL, dropWhileR, empty, filter,
+                      fromList, length, null, singleton, (<|), (><), (|>))
+
+-- Base
+import           Data.Bool     (Bool (..))
+import           Data.Foldable (Foldable)
+import qualified Data.Foldable as Foldable
+import           Data.Functor  (fmap)
+import qualified Data.List     as List
+import           Data.Maybe    (Maybe (..))
+import           Data.Ord      (Ord)
+import           Prelude       (Num (..))
+
+-- | Alias for '><'.
+append :: Seq a -> Seq a -> Seq a
+append = (><)
+
+-- | Like 'List.intersperse', but for 'Seq'.
+intersperse :: a -> Seq a -> Seq a
+intersperse x xs =
+  fromList (List.intersperse x (Foldable.toList xs))
+
+-- | 'Foldable.fold' specialized for 'Seq'.
+concat :: Foldable f => f (Seq a) -> Seq a
+concat = Foldable.fold
+
+-- | Like 'Foldable.concatMap', but for 'Seq'.
+concatMap :: Foldable f => (a -> Seq b) -> f a -> Seq b
+concatMap f xs = concat (fmap f (Foldable.toList xs))
+
+{- |
+
+>>> minimum (fromList [1,2,3])
+Just 1
+
+>>> minimum empty
+Nothing
+
+-}
+minimum :: Ord a => Seq a -> Maybe a
+minimum xs =
+  if null xs then Nothing else Just (List.minimum (Foldable.toList xs))
+
+{- |
+
+>>> maximum (fromList [1,2,3])
+Just 3
+
+>>> maximum empty
+Nothing
+
+-}
+maximum :: Ord a => Seq a -> Maybe a
+maximum xs =
+  if null xs then Nothing else Just (List.maximum (Foldable.toList xs))
+
+-- | Specialization of 'fmap'.
+map :: (a -> b) -> Seq a -> Seq b
+map = fmap
+
+-- | Specialization of 'Foldable.toList' for 'Seq'.
+toList :: Seq a -> [a]
+toList = Foldable.toList
+
+{- |
+
+>>> adjustLast (+ 1) (fromList [1, 2, 3])
+fromList [1,2,4]
+
+>>> adjustLast (+ 1) empty
+fromList []
+
+-}
+adjustLast :: (a -> a) -> Seq a -> Seq a
+adjustLast f xs = adjust f (length xs - 1) xs
+
+trimWhile :: (a -> Bool) -> Seq a -> Seq a
+trimWhile f xs = dropWhileL f (dropWhileR f xs)
diff --git a/src/Bricks/Internal/Text.hs b/src/Bricks/Internal/Text.hs
new file mode 100644
--- /dev/null
+++ b/src/Bricks/Internal/Text.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Bricks.Internal.Text
+
+  ( Text
+
+  , all
+  , append
+  , concat
+  , concatMap
+  , intercalate
+  , intercalateMap
+  , isPrefixOf
+  , isSuffixOf
+  , null
+  , pack
+  , replace
+  , replicate
+  , show
+  , singleton
+  , unpack
+  , unwords
+
+  ) where
+
+-- Text
+import           Data.Text (Text, all, append, isPrefixOf, isSuffixOf, null,
+                            pack, replace, replicate, singleton, unpack,
+                            unwords)
+import qualified Data.Text as Text
+
+-- Base
+import           Data.Foldable (Foldable, foldr, toList)
+import           Data.Function ((.))
+import           Data.Functor  (Functor, fmap)
+import qualified Text.Show
+
+concat :: Foldable f => f Text -> Text
+concat =
+  Text.concat . toList
+
+concatMap :: Foldable f => (a -> Text) -> f a -> Text
+concatMap f = foldr (append . f) Text.empty
+
+intercalate :: Foldable f => Text -> f Text -> Text
+intercalate x =
+  Text.intercalate x . toList
+
+intercalateMap :: (Foldable f, Functor f) => Text -> (a -> Text) -> f a -> Text
+intercalateMap i f =
+  intercalate i . fmap f
+
+show :: Text.Show.Show a => a -> Text
+show = Text.pack . Text.Show.show
diff --git a/test/doctest.hs b/test/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest.hs
@@ -0,0 +1,4 @@
+import Test.DocTest (doctest)
+
+main :: IO ()
+main = doctest ["src", "-XOverloadedStrings"]
