diff --git a/protolude.cabal b/protolude.cabal
--- a/protolude.cabal
+++ b/protolude.cabal
@@ -1,5 +1,5 @@
 name:                protolude
-version:             0.2
+version:             0.2.1
 synopsis:            A small prelude.
 description:         A sensible set of defaults for writing custom Preludes.
 homepage:            https://github.com/sdiehl/protolude
@@ -11,6 +11,7 @@
 category:            Prelude
 build-type:          Simple
 cabal-version:       >=1.10
+bug-reports:         https://github.com/sdiehl/protolude/issues
 tested-with:         
   GHC == 7.6.1,
   GHC == 7.6.2,
@@ -24,7 +25,6 @@
   GHC == 7.10.3,
   GHC == 8.0.1,
   GHC == 8.2.1
-Bug-Reports:         https://github.com/sdiehl/protolude/issues
 
 description:
     A sensible set of defaults for writing custom Preludes.
@@ -36,24 +36,22 @@
   exposed-modules:     
     Protolude
     Unsafe
-
-  other-modules:
-    Exceptions
-    Base
-    Applicative
-    Bool
     Debug
-    List
-    Monad
-    Show
-    Conv
-    Either
-    Functor
-    Semiring
-    Bifunctor
-    CallStack
-    Error
-    Panic
+    Protolude.Exceptions
+    Protolude.Base
+    Protolude.Applicative
+    Protolude.Bool
+    Protolude.List
+    Protolude.Monad
+    Protolude.Show
+    Protolude.Conv
+    Protolude.Either
+    Protolude.Functor
+    Protolude.Semiring
+    Protolude.Bifunctor
+    Protolude.CallStack
+    Protolude.Error
+    Protolude.Panic
 
   default-extensions:
     NoImplicitPrelude
@@ -66,23 +64,24 @@
     -fwarn-implicit-prelude
 
   build-depends:       
-    base             >= 4.6  && <4.11,
-    array            >= 0.4  && <0.6,
-    ghc-prim         >= 0.3  && <0.6,
-    async            >= 2.0  && <2.2,
-    deepseq          >= 1.3  && <1.5,
-    containers       >= 0.5  && <0.6,
-    hashable         >= 1.2  && <1.3,
-    transformers     >= 0.2  && <0.6,
-    text             >= 1.2  && <1.3,
-    stm              >= 2.4  && <2.5,
-    bytestring       >= 0.10 && <0.11,
-    mtl              >= 2.1  && <2.3,
-    mtl-compat       >= 0.2  && <0.3
+    base                >= 4.6  && <4.11,
+    array               >= 0.4  && <0.6,
+    ghc-prim            >= 0.3  && <0.6,
+    async               >= 2.0  && <2.2,
+    deepseq             >= 1.3  && <1.5,
+    containers          >= 0.5  && <0.6,
+    hashable            >= 1.2  && <1.3,
+    transformers        >= 0.2  && <0.6,
+    text                >= 1.2  && <1.3,
+    stm                 >= 2.4  && <2.5,
+    bytestring          >= 0.10 && <0.11,
+    mtl                 >= 2.1  && <2.3,
+    mtl-compat          >= 0.2  && <0.3,
+    transformers-compat >= 0.4  && <0.6
 
   if impl(ghc >= 7.8.0)
     build-depends:
-      safe             >= 0.3  && <0.3.16
+      safe             >= 0.3  && <0.4
   else
     build-depends:
       safe             >= 0.3  && <0.3.10
diff --git a/src/Applicative.hs b/src/Applicative.hs
deleted file mode 100644
--- a/src/Applicative.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Applicative (
-  orAlt,
-  orEmpty,
-  eitherA,
-  purer,
-  liftAA2,
-  (<<*>>),
-) where
-
-import Data.Bool (Bool)
-import Data.Function ((.))
-import Data.Either (Either(..))
-import Data.Monoid (Monoid(..))
-import Control.Applicative
-
-orAlt :: (Alternative f, Monoid a) => f a -> f a
-orAlt f = f <|> pure mempty
-
-orEmpty :: Alternative f => Bool -> a -> f a
-orEmpty b a = if b then pure a else empty
-
-eitherA :: (Alternative f) => f a -> f b -> f (Either a b)
-eitherA a b = (Left <$> a) <|> (Right <$> b)
-
-purer :: (Applicative f, Applicative g) => a -> f (g a)
-purer = pure . pure
-
-liftAA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)
-liftAA2 = liftA2 . liftA2
-
-(<<*>>) :: (Applicative f, Applicative g)  => f (g (a -> b)) -> f (g a) -> f (g b)
-(<<*>>) = liftA2 (<*>)
diff --git a/src/Base.hs b/src/Base.hs
deleted file mode 100644
--- a/src/Base.hs
+++ /dev/null
@@ -1,137 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Unsafe #-}
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE ExplicitNamespaces #-}
-
-module Base (
-  module X,
-  ($!),
-) where
-
--- Glorious Glasgow Haskell Compiler
-#if defined(__GLASGOW_HASKELL__) && ( __GLASGOW_HASKELL__ >= 600 )
-
--- Base GHC types
-import GHC.Num as X (
-    Num(..)
-  , Integer
-  , subtract
-  )
-import GHC.Enum as X (
-    Bounded(..)
-  , Enum(..)
-  , boundedEnumFrom
-  , boundedEnumFromThen
-  )
-import GHC.Real as X
-import GHC.Float as X (
-    Float(..)
-  , Double(..)
-  , Floating (..)
-  , RealFloat(..)
-  , showFloat
-  , showSignedFloat
-  )
-import GHC.Show as X (
-    Show(..)
-  )
-import GHC.Exts as X (
-    Constraint
-  , Ptr
-  , FunPtr
-  )
-import GHC.Base as X (
-    (++)
-  , seq
-  , asTypeOf
-  , ord
-  , maxInt
-  , minInt
-  , until
-  )
-
--- Exported for lifting into new functions.
-import System.IO as X (
-    print
-  , putStr
-  , putStrLn
-  )
-
-import GHC.Types as X (
-    Bool
-  , Char
-  , Int
-  , Word
-  , Ordering
-  , IO
-#if ( __GLASGOW_HASKELL__ >= 710 )
-  , Coercible
-#endif
-  )
-
-#if ( __GLASGOW_HASKELL__ >= 710 )
-import GHC.StaticPtr as X (StaticPtr)
-#endif
-
-#if ( __GLASGOW_HASKELL__ >= 800 )
-import GHC.OverloadedLabels as X (
-    IsLabel(..)
-  )
-
-import GHC.ExecutionStack as X (
-    Location(..)
-  , SrcLoc(..)
-  , getStackTrace
-  , showStackTrace
-  )
-
-import GHC.Stack as X (
-    CallStack
-  , type HasCallStack
-  , callStack
-  , prettySrcLoc
-  , currentCallStack
-  , getCallStack
-  , prettyCallStack
-  , withFrozenCallStack
-  )
-
-#if ( __GLASGOW_HASKELL__ >= 710 )
-import GHC.TypeLits as X (
-    Symbol
-  , SomeSymbol(..)
-  , Nat
-  , SomeNat(..)
-  , CmpNat
-  , KnownSymbol
-  , KnownNat
-  , natVal
-  , someNatVal
-  , symbolVal
-  , someSymbolVal
-  )
-#endif
-
--- Pending GHC 8.2 we'll expose these.
-
-{-
-import GHC.Records as X (
-    HasField(..)
-  )
-
-import Data.Kind as X (
-    type (*)
-  , type Type
-  )
--}
-
-#endif
-
--- Default Prelude defines this at the toplevel module, so we do as well.
-infixr 0 $!
-
-($!) :: (a -> b) -> a -> b
-f $! x  = let !vx = x in f vx
-
-#endif
diff --git a/src/Bifunctor.hs b/src/Bifunctor.hs
deleted file mode 100644
--- a/src/Bifunctor.hs
+++ /dev/null
@@ -1,47 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Bifunctor (
-  Bifunctor(..)
-) where
-
-import Data.Function (id, (.))
-import Data.Either (Either(..))
-import Control.Applicative ( Const(..) )
-
-class Bifunctor p where
-  {-# MINIMAL bimap | first, second #-}
-
-  bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
-  bimap f g = first f . second g
-
-  first :: (a -> b) -> p a c -> p b c
-  first f = bimap f id
-
-  second :: (b -> c) -> p a b -> p a c
-  second = bimap id
-
-instance Bifunctor (,) where
-  bimap f g ~(a, b) = (f a, g b)
-
-instance Bifunctor ((,,) x1) where
-  bimap f g ~(x1, a, b) = (x1, f a, g b)
-
-instance Bifunctor ((,,,) x1 x2) where
-  bimap f g ~(x1, x2, a, b) = (x1, x2, f a, g b)
-
-instance Bifunctor ((,,,,) x1 x2 x3) where
-  bimap f g ~(x1, x2, x3, a, b) = (x1, x2, x3, f a, g b)
-
-instance Bifunctor ((,,,,,) x1 x2 x3 x4) where
-  bimap f g ~(x1, x2, x3, x4, a, b) = (x1, x2, x3, x4, f a, g b)
-
-instance Bifunctor ((,,,,,,) x1 x2 x3 x4 x5) where
-  bimap f g ~(x1, x2, x3, x4, x5, a, b) = (x1, x2, x3, x4, x5, f a, g b)
-
-instance Bifunctor Either where
-  bimap f _ (Left a) = Left (f a)
-  bimap _ g (Right b) = Right (g b)
-
-instance Bifunctor Const where
-  bimap f _ (Const a) = Const (f a)
diff --git a/src/Bool.hs b/src/Bool.hs
deleted file mode 100644
--- a/src/Bool.hs
+++ /dev/null
@@ -1,64 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Bool (
-  whenM
-, unlessM
-, ifM
-, guardM
-, bool
-, (&&^)
-, (||^)
-, (<&&>)
-, (<||>)
-) where
-
-import Data.Bool (Bool(..), (&&), (||))
-import Data.Function (flip)
-import Control.Applicative(Applicative, liftA2)
-import Control.Monad (Monad, MonadPlus, return, when, unless, guard, (>>=), (=<<))
-
-bool :: a -> a -> Bool -> a
-bool f t p = if p then t else f
-
-whenM :: Monad m => m Bool -> m () -> m ()
-whenM p m =
-  p >>= flip when m
-
-unlessM :: Monad m => m Bool -> m () -> m ()
-unlessM p m =
-  p >>= flip unless m
-
-ifM :: Monad m => m Bool -> m a -> m a -> m a
-ifM p x y = p >>= \b -> if b then x else y
-
-guardM :: MonadPlus m => m Bool -> m ()
-guardM f = guard =<< f
-
--- | The '||' operator lifted to a monad. If the first
---   argument evaluates to 'True' the second argument will not
---   be evaluated.
-infixr 2 ||^ -- same as (||)
-(||^) :: Monad m => m Bool -> m Bool -> m Bool
-(||^) a b = ifM a (return True) b
-
-infixr 2 <||>
--- | '||' lifted to an Applicative.
--- Unlike '||^' the operator is __not__ short-circuiting.
-(<||>) :: Applicative a => a Bool -> a Bool -> a Bool
-(<||>) = liftA2 (||)
-{-# INLINE (<||>) #-}
-
--- | The '&&' operator lifted to a monad. If the first
---   argument evaluates to 'False' the second argument will not
---   be evaluated.
-infixr 3 &&^ -- same as (&&)
-(&&^) :: Monad m => m Bool -> m Bool -> m Bool
-(&&^) a b = ifM a b (return False)
-
-infixr 3 <&&>
--- | '&&' lifted to an Applicative.
--- Unlike '&&^' the operator is __not__ short-circuiting.
-(<&&>) :: Applicative a => a Bool -> a Bool -> a Bool
-(<&&>) = liftA2 (&&)
-{-# INLINE (<&&>) #-}
diff --git a/src/CallStack.hs b/src/CallStack.hs
deleted file mode 100644
--- a/src/CallStack.hs
+++ /dev/null
@@ -1,18 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE KindSignatures #-}
-{-# LANGUAGE ImplicitParams #-}
-{-# LANGUAGE ConstraintKinds #-}
-
-module CallStack
-( HasCallStack
-) where
-
-#if MIN_VERSION_base(4,9,0)
-import GHC.Stack (HasCallStack)
-#elif MIN_VERSION_base(4,8,1)
-import qualified GHC.Stack
-type HasCallStack = (?callStack :: GHC.Stack.CallStack)
-#else
-import GHC.Exts (Constraint)
-type HasCallStack = (() :: Constraint)
-#endif
diff --git a/src/Conv.hs b/src/Conv.hs
deleted file mode 100644
--- a/src/Conv.hs
+++ /dev/null
@@ -1,75 +0,0 @@
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-
-module Conv (
-  StringConv (..)
-, toS
-, toSL
-, Leniency (..)
-) where
-
-import Data.ByteString.Char8      as B
-import Data.ByteString.Lazy.Char8 as LB
-import Data.Text                  as T
-import Data.Text.Encoding         as T
-import Data.Text.Encoding.Error   as T
-import Data.Text.Lazy             as LT
-import Data.Text.Lazy.Encoding    as LT
-
-import Base
-import Data.Eq (Eq(..))
-import Data.Ord (Ord(..))
-import Data.Function ((.), id)
-import Data.String (String)
-import Control.Applicative (pure)
-
-data Leniency = Lenient | Strict
-  deriving (Eq,Show,Ord,Enum,Bounded)
-
-class StringConv a b where
-  strConv :: Leniency -> a -> b
-
-toS :: StringConv a b => a -> b
-toS = strConv Strict
-
-toSL :: StringConv a b => a -> b
-toSL = strConv Lenient
-
-instance StringConv String String where strConv _ = id
-instance StringConv String B.ByteString where strConv _ = B.pack
-instance StringConv String LB.ByteString where strConv _ = LB.pack
-instance StringConv String T.Text where strConv _ = T.pack
-instance StringConv String LT.Text where strConv _ = LT.pack
-
-instance StringConv B.ByteString String where strConv _ = B.unpack
-instance StringConv B.ByteString B.ByteString where strConv _ = id
-instance StringConv B.ByteString LB.ByteString where strConv _ = LB.fromChunks . pure
-instance StringConv B.ByteString T.Text where strConv = decodeUtf8T
-instance StringConv B.ByteString LT.Text where strConv l = strConv l . LB.fromChunks . pure
-
-instance StringConv LB.ByteString String where strConv _ = LB.unpack
-instance StringConv LB.ByteString B.ByteString where strConv _ = B.concat . LB.toChunks
-instance StringConv LB.ByteString LB.ByteString where strConv _ = id
-instance StringConv LB.ByteString T.Text where strConv l = decodeUtf8T l . strConv l
-instance StringConv LB.ByteString LT.Text where strConv = decodeUtf8LT
-
-instance StringConv T.Text String where strConv _ = T.unpack
-instance StringConv T.Text B.ByteString where strConv _ = T.encodeUtf8
-instance StringConv T.Text LB.ByteString where strConv l = strConv l . T.encodeUtf8
-instance StringConv T.Text LT.Text where strConv _ = LT.fromStrict
-instance StringConv T.Text T.Text where strConv _ = id
-
-instance StringConv LT.Text String where strConv _ = LT.unpack
-instance StringConv LT.Text T.Text where strConv _ = LT.toStrict
-instance StringConv LT.Text LT.Text where strConv _ = id
-instance StringConv LT.Text LB.ByteString where strConv _ = LT.encodeUtf8
-instance StringConv LT.Text B.ByteString where strConv l = strConv l . LT.encodeUtf8
-
-decodeUtf8T :: Leniency -> B.ByteString -> T.Text
-decodeUtf8T Lenient = T.decodeUtf8With T.lenientDecode
-decodeUtf8T Strict = T.decodeUtf8With T.strictDecode
-
-decodeUtf8LT :: Leniency -> LB.ByteString -> LT.Text
-decodeUtf8LT Lenient = LT.decodeUtf8With T.lenientDecode
-decodeUtf8LT Strict = LT.decodeUtf8With T.strictDecode
diff --git a/src/Debug.hs b/src/Debug.hs
--- a/src/Debug.hs
+++ b/src/Debug.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE Trustworthy #-}
 {-# LANGUAGE NoImplicitPrelude #-}
+{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 
 module Debug (
   undefined,
@@ -16,9 +17,9 @@
 import Data.Text (Text, unpack)
 import Control.Monad (Monad, return)
 
-import qualified Base as P
-import Error (error)
-import Show (Print, hPutStrLn)
+import qualified Protolude.Base as P
+import Protolude.Error (error)
+import Protolude.Show (Print, hPutStrLn)
 
 import System.IO(stderr)
 import System.IO.Unsafe (unsafePerformIO)
@@ -55,8 +56,10 @@
 traceId :: Text -> Text
 traceId s = trace s s
 
+{-# WARNING notImplemented "'notImplemented' remains in code" #-}
 notImplemented :: a
 notImplemented = error "Not implemented"
 
+{-# WARNING undefined "'undefined' remains in code" #-}
 undefined :: a
 undefined = error "Prelude.undefined"
diff --git a/src/Either.hs b/src/Either.hs
deleted file mode 100644
--- a/src/Either.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Either (
-  maybeToLeft
-, maybeToRight
-, leftToMaybe
-, rightToMaybe
-, maybeEmpty
-, maybeToEither
-) where
-
-import Data.Function (const)
-import Data.Monoid (Monoid, mempty)
-import Data.Maybe (Maybe(..), maybe)
-import Data.Either (Either(..), either)
-
-leftToMaybe :: Either l r -> Maybe l
-leftToMaybe = either Just (const Nothing)
-
-rightToMaybe :: Either l r -> Maybe r
-rightToMaybe = either (const Nothing) Just
-
-maybeToRight :: l -> Maybe r -> Either l r
-maybeToRight l = maybe (Left l) Right
-
-maybeToLeft :: r -> Maybe l -> Either l r
-maybeToLeft r = maybe (Right r) Left
-
-maybeEmpty :: Monoid b => (a -> b) -> Maybe a -> b
-maybeEmpty = maybe mempty
-
-maybeToEither :: e -> Maybe a -> Either e a
-maybeToEither e Nothing = Left e
-maybeToEither _ (Just a) = Right a
diff --git a/src/Error.hs b/src/Error.hs
deleted file mode 100644
--- a/src/Error.hs
+++ /dev/null
@@ -1,45 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE PolyKinds #-}
-{-# LANGUAGE ImplicitParams #-}
-{-# LANGUAGE ExistentialQuantification #-}
-
-#if MIN_VERSION_base(4,9,0)
-{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
-#endif
-
-module Error
-( error
-) where
-
-import GHC.Prim
-import Data.Text (Text, unpack)
-
-#if MIN_VERSION_base(4,9,0)
--- Full stack trace.
-
-import GHC.Types (RuntimeRep)
-import CallStack (HasCallStack)
-import GHC.Exception (errorCallWithCallStackException)
-
-error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack => Text -> a
-error s = raise# (errorCallWithCallStackException (unpack s) ?callstack)
-
-#elif MIN_VERSION_base(4,7,0)
--- Basic Call Stack with callsite.
-
-import GHC.Exception (errorCallException)
-
-error :: Text -> a
-error s = raise# (errorCallException (unpack s))
-
-#else
-
--- No exception tracing.
-import GHC.Types
-import GHC.Exception
-
-error :: Text -> a
-error s = throw (ErrorCall (unpack s))
-
-#endif
diff --git a/src/Exceptions.hs b/src/Exceptions.hs
deleted file mode 100644
--- a/src/Exceptions.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Exceptions (
-  hush,
-  note,
-  tryIO,
-) where
-
-import Base (IO)
-import Data.Function ((.))
-import Control.Monad.Trans (liftIO)
-import Control.Monad.IO.Class (MonadIO)
-import Control.Monad.Except (ExceptT(..), MonadError, throwError)
-import Control.Exception as Exception
-import Control.Applicative
-import Data.Maybe (Maybe, maybe)
-import Data.Either (Either(..))
-
-hush :: Alternative m => Either e a -> m a
-hush (Left _)  = empty
-hush (Right x) = pure x
-
--- To suppress redundenet applicative constraint warning on GHC 8.0
-#if ( __GLASGOW_HASKELL__ >= 800 )
-note :: (MonadError e m) => e -> Maybe a -> m a
-note err = maybe (throwError err) pure
-#else
-note :: (MonadError e m, Applicative m) => e -> Maybe a -> m a
-note err = maybe (throwError err) pure
-#endif
-
-tryIO :: MonadIO m => IO a -> ExceptT IOException m a
-tryIO = ExceptT . liftIO . Exception.try
diff --git a/src/Functor.hs b/src/Functor.hs
deleted file mode 100644
--- a/src/Functor.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Functor (
-  Functor(..),
-  ($>),
-  (<$>),
-  (<<$>>),
-  void,
-) where
-
-import Data.Function ((.))
-
-#if MIN_VERSION_base(4,7,0)
-import Data.Functor (
-    Functor(..)
-  , ($>)
-  , (<$>)
-  , void
-  )
-#else
-import Data.Functor (
-    Functor(..)
-  , (<$>)
-  )
-
-import Data.Function (flip)
-
-infixl 4 $>
-
-($>) :: Functor f => f a -> b -> f b
-($>) = flip (<$)
-
-void :: Functor f => f a -> f ()
-void x = () <$ x
-#endif
-
-infixl 4 <<$>>
-
-(<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
-(<<$>>) = fmap . fmap
diff --git a/src/List.hs b/src/List.hs
deleted file mode 100644
--- a/src/List.hs
+++ /dev/null
@@ -1,50 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module List (
-  head,
-  ordNub,
-  sortOn,
-  list,
-  product,
-  sum
-) where
-
-import Data.List (sortBy)
-import Data.Maybe (Maybe(..))
-import Data.Ord (Ord, comparing)
-import Data.Foldable (Foldable, foldr, foldl')
-import Data.Function ((.))
-import Data.Functor (fmap)
-import Control.Applicative (pure)
-import qualified Data.Set as Set
-import GHC.Num (Num, (+), (*))
-
-head :: (Foldable f) => f a -> Maybe a
-head = foldr (\x _ -> pure x) Nothing
-
-sortOn :: (Ord o) => (a -> o) -> [a] -> [a]
-sortOn = sortBy . comparing
-
--- O(n * log n)
-ordNub :: (Ord a) => [a] -> [a]
-ordNub l = go Set.empty l
-  where
-    go _ []     = []
-    go s (x:xs) =
-      if x `Set.member` s
-      then go s xs
-      else x : go (Set.insert x s) xs
-
-list :: [b] -> (a -> b) -> [a] -> [b]
-list def f xs = case xs of
-  [] -> def
-  _  -> fmap f xs
-
-{-# INLINE product #-}
-product :: (Foldable f, Num a) => f a -> a
-product = foldl' (*) 1
-
-{-# INLINE sum #-}
-sum :: (Foldable f, Num a) => f a -> a
-sum = foldl' (+) 0
diff --git a/src/Monad.hs b/src/Monad.hs
deleted file mode 100644
--- a/src/Monad.hs
+++ /dev/null
@@ -1,69 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Monad (
-    Monad((>>=), return)
-  , MonadPlus(..)
-
-  , (=<<)
-  , (>=>)
-  , (<=<)
-  , (>>)
-  , forever
-
-  , join
-  , mfilter
-  , filterM
-  , mapAndUnzipM
-  , zipWithM
-  , zipWithM_
-  , foldM
-  , foldM_
-  , replicateM
-  , replicateM_
-  , concatMapM
-
-  , guard
-  , when
-  , unless
-
-  , liftM
-  , liftM2
-  , liftM3
-  , liftM4
-  , liftM5
-  , liftM'
-  , liftM2'
-  , ap
-
-  , (<$!>)
-  ) where
-
-import Base (seq)
-import Data.List (concat)
-import Control.Monad
-
-concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
-concatMapM f xs = liftM concat (mapM f xs)
-
-liftM' :: Monad m => (a -> b) -> m a -> m b
-liftM' = (<$!>)
-{-# INLINE liftM' #-}
-
-liftM2' :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
-liftM2' f a b = do
-  x <- a
-  y <- b
-  let z = f x y
-  z `seq` return z
-{-# INLINE liftM2' #-}
-
-#if !MIN_VERSION_base(4,8,0)
-(<$!>) :: Monad m => (a -> b) -> m a -> m b
-f <$!> m = do
-  x <- m
-  let z = f x
-  z `seq` return z
-{-# INLINE (<$!>) #-}
-#endif
diff --git a/src/Panic.hs b/src/Panic.hs
deleted file mode 100644
--- a/src/Panic.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE DeriveDataTypeable #-}
-#if MIN_VERSION_base(4,9,0)
-{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
-#endif
-
-module Panic (
-  FatalError(..),
-  panic,
-) where
-
-import Base (Show)
-import Data.Text (Text)
-import Data.Typeable (Typeable)
-import CallStack (HasCallStack)
-import Control.Exception as X
-
--- | Uncatchable exceptions thrown and never caught.
-data FatalError = FatalError { fatalErrorMessage :: Text }
-  deriving (Show, Typeable)
-
-instance Exception FatalError
-
-panic :: HasCallStack => Text -> a
-panic a = throw (FatalError a)
diff --git a/src/Protolude.hs b/src/Protolude.hs
--- a/src/Protolude.hs
+++ b/src/Protolude.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE Trustworthy #-}
 {-# LANGUAGE FlexibleContexts #-}
@@ -27,25 +28,26 @@
   liftIO2,
 #if !MIN_VERSION_base(4,8,0)
   (&),
+  scanl',
 #endif
   die,
 ) where
 
 -- Protolude module exports.
-import List as X
-import Show as X
-import Bool as X
 import Debug as X
-import Monad as X
-import Functor as X
-import Either as X
-import Applicative as X
-import Conv as X
-import Panic as X
-import Exceptions as X
-import Semiring as X
+import Protolude.List as X
+import Protolude.Show as X
+import Protolude.Bool as X
+import Protolude.Monad as X
+import Protolude.Functor as X
+import Protolude.Either as X
+import Protolude.Applicative as X
+import Protolude.Conv as X
+import Protolude.Panic as X
+import Protolude.Exceptions as X
+import Protolude.Semiring as X
 
-import Base as Base hiding (
+import Protolude.Base as Base hiding (
     putStr           -- Overriden by Show.putStr
   , putStrLn         -- Overriden by Show.putStrLn
   , print            -- Overriden by Protolude.print
@@ -56,7 +58,7 @@
   , showSignedFloat  -- Custom Show instances deprecated.
   , showsPrec        -- Custom Show instances deprecated.
   )
-import qualified Base as PBase
+import qualified Protolude.Base as PBase
 
 -- Used for 'show', not exported.
 import Data.String (String)
@@ -138,7 +140,7 @@
 import Data.Monoid as X
 
 #if !MIN_VERSION_base(4,8,0)
-import Bifunctor as X (Bifunctor(..))
+import Protolude.Bifunctor as X (Bifunctor(..))
 #else
 import Data.Bifunctor as X (Bifunctor(..))
 #endif
@@ -155,10 +157,10 @@
 import Data.Tuple as X (
     fst
   , snd
-  , curry 
+  , curry
   , uncurry
   , swap
-  ) 
+  )
 
 import Data.List as X (
     splitAt
@@ -177,6 +179,9 @@
   , subsequences
   , permutations
   , scanl
+#if MIN_VERSION_base(4,8,0)
+  , scanl'
+#endif
   , scanr
   , iterate
   , repeat
@@ -197,6 +202,12 @@
   , genericReplicate
   )
 
+#if !MIN_VERSION_base(4,8,0)
+-- These imports are required for the scanl' rewrite rules
+import GHC.Exts (build)
+import Data.List (tail)
+#endif
+
 -- Hashing
 import Data.Hashable as X (
     Hashable
@@ -282,6 +293,11 @@
   , runReaderT
   )
 
+import Control.Monad.Trans.Except as X (
+    throwE
+  , catchE
+  )
+
 import Control.Monad.Except as X (
     MonadError
   , Except
@@ -290,6 +306,10 @@
   , catchError
   , runExcept
   , runExceptT
+  , mapExcept
+  , mapExceptT
+  , withExcept
+  , withExceptT
   )
 
 import Control.Monad.Trans as X (
@@ -325,7 +345,7 @@
 
 import Data.Either as X (
     Either(..)
-  , either 
+  , either
   , lefts
   , rights
   , partitionEithers
@@ -431,7 +451,7 @@
 
 -- IO
 import System.Environment as X (getArgs)
-import qualified System.Exit 
+import qualified System.Exit
 import System.Exit as X (
     ExitCode(..)
   , exitWith
@@ -475,7 +495,7 @@
   , retry
   , orElse
   , check
-  , throwSTM 
+  , throwSTM
   , catchSTM
   )
 import Control.Concurrent as X hiding (
@@ -494,7 +514,7 @@
   , wait
   , poll
   , waitCatch
-  , cancel 
+  , cancel
   , cancelWith
   , asyncThreadId
   , waitAny
@@ -609,4 +629,33 @@
 #else
 die :: Text -> IO a
 die err = hPutStrLn stderr err >> exitFailure
+#endif
+
+#if !MIN_VERSION_base(4,8,0)
+-- This is a literal copy of the implementation in GHC.List in base-4.10.1.0.
+
+-- | A strictly accumulating version of 'scanl'
+{-# NOINLINE [1] scanl' #-}
+scanl'           :: (b -> a -> b) -> b -> [a] -> [b]
+scanl' = scanlGo'
+  where
+    scanlGo'           :: (b -> a -> b) -> b -> [a] -> [b]
+    scanlGo' f !q ls    = q : (case ls of
+                            []   -> []
+                            x:xs -> scanlGo' f (f q x) xs)
+
+{-# RULES
+"scanl'"  [~1] forall f a bs . scanl' f a bs =
+  build (\c n -> a `c` foldr (scanlFB' f c) (flipSeqScanl' n) bs a)
+"scanlList'" [1] forall f a bs .
+    foldr (scanlFB' f (:)) (flipSeqScanl' []) bs a = tail (scanl' f a bs)
+ #-}
+
+{-# INLINE [0] scanlFB' #-}
+scanlFB' :: (b -> a -> b) -> (b -> c -> c) -> a -> (b -> c) -> b -> c
+scanlFB' f c = \b g -> \x -> let !b' = f x b in b' `c` g b'
+
+{-# INLINE [0] flipSeqScanl' #-}
+flipSeqScanl' :: a -> b -> a
+flipSeqScanl' a !_b = a
 #endif
diff --git a/src/Protolude/Applicative.hs b/src/Protolude/Applicative.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Applicative.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Applicative (
+  orAlt,
+  orEmpty,
+  eitherA,
+  purer,
+  liftAA2,
+  (<<*>>),
+) where
+
+import Data.Bool (Bool)
+import Data.Function ((.))
+import Data.Either (Either(..))
+import Data.Monoid (Monoid(..))
+import Control.Applicative
+
+orAlt :: (Alternative f, Monoid a) => f a -> f a
+orAlt f = f <|> pure mempty
+
+orEmpty :: Alternative f => Bool -> a -> f a
+orEmpty b a = if b then pure a else empty
+
+eitherA :: (Alternative f) => f a -> f b -> f (Either a b)
+eitherA a b = (Left <$> a) <|> (Right <$> b)
+
+purer :: (Applicative f, Applicative g) => a -> f (g a)
+purer = pure . pure
+
+liftAA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)
+liftAA2 = liftA2 . liftA2
+
+(<<*>>) :: (Applicative f, Applicative g)  => f (g (a -> b)) -> f (g a) -> f (g b)
+(<<*>>) = liftA2 (<*>)
diff --git a/src/Protolude/Base.hs b/src/Protolude/Base.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Base.hs
@@ -0,0 +1,135 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Unsafe #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE ExplicitNamespaces #-}
+
+module Protolude.Base (
+  module X,
+  ($!),
+) where
+
+-- Glorious Glasgow Haskell Compiler
+#if defined(__GLASGOW_HASKELL__) && ( __GLASGOW_HASKELL__ >= 600 )
+
+-- Base GHC types
+import GHC.Num as X (
+    Num(..)
+  , Integer
+  , subtract
+  )
+import GHC.Enum as X (
+    Bounded(..)
+  , Enum(..)
+  , boundedEnumFrom
+  , boundedEnumFromThen
+  )
+import GHC.Real as X
+import GHC.Float as X (
+    Float(..)
+  , Double(..)
+  , Floating (..)
+  , RealFloat(..)
+  , showFloat
+  , showSignedFloat
+  )
+import GHC.Show as X (
+    Show(..)
+  )
+import GHC.Exts as X (
+    Constraint
+  , Ptr
+  , FunPtr
+  )
+import GHC.Base as X (
+    (++)
+  , seq
+  , asTypeOf
+  , ord
+  , maxInt
+  , minInt
+  , until
+  )
+
+-- Exported for lifting into new functions.
+import System.IO as X (
+    print
+  , putStr
+  , putStrLn
+  )
+
+import GHC.Types as X (
+    Bool
+  , Char
+  , Int
+  , Word
+  , Ordering
+  , IO
+#if ( __GLASGOW_HASKELL__ >= 710 )
+  , Coercible
+#endif
+  )
+
+#if ( __GLASGOW_HASKELL__ >= 710 )
+import GHC.StaticPtr as X (StaticPtr)
+#endif
+
+#if ( __GLASGOW_HASKELL__ >= 800 )
+import GHC.OverloadedLabels as X (
+    IsLabel(..)
+  )
+
+import GHC.ExecutionStack as X (
+    Location(..)
+  , SrcLoc(..)
+  , getStackTrace
+  , showStackTrace
+  )
+
+import GHC.Stack as X (
+    CallStack
+  , type HasCallStack
+  , callStack
+  , prettySrcLoc
+  , currentCallStack
+  , getCallStack
+  , prettyCallStack
+  , withFrozenCallStack
+  )
+
+#if ( __GLASGOW_HASKELL__ >= 710 )
+import GHC.TypeLits as X (
+    Symbol
+  , SomeSymbol(..)
+  , Nat
+  , SomeNat(..)
+  , CmpNat
+  , KnownSymbol
+  , KnownNat
+  , natVal
+  , someNatVal
+  , symbolVal
+  , someSymbolVal
+  )
+#endif
+
+#if ( __GLASGOW_HASKELL__ >= 802 )
+import GHC.Records as X (
+    HasField(..)
+  )
+
+import Data.Kind as X (
+    type (*)
+  , type Type
+  )
+#endif
+
+#endif
+
+-- Default Prelude defines this at the toplevel module, so we do as well.
+infixr 0 $!
+
+($!) :: (a -> b) -> a -> b
+f $! x  = let !vx = x in f vx
+
+#endif
diff --git a/src/Protolude/Bifunctor.hs b/src/Protolude/Bifunctor.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Bifunctor.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Bifunctor (
+  Bifunctor(..)
+) where
+
+import Data.Function (id, (.))
+import Data.Either (Either(..))
+import Control.Applicative ( Const(..) )
+
+class Bifunctor p where
+  {-# MINIMAL bimap | first, second #-}
+
+  bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
+  bimap f g = first f . second g
+
+  first :: (a -> b) -> p a c -> p b c
+  first f = bimap f id
+
+  second :: (b -> c) -> p a b -> p a c
+  second = bimap id
+
+instance Bifunctor (,) where
+  bimap f g ~(a, b) = (f a, g b)
+
+instance Bifunctor ((,,) x1) where
+  bimap f g ~(x1, a, b) = (x1, f a, g b)
+
+instance Bifunctor ((,,,) x1 x2) where
+  bimap f g ~(x1, x2, a, b) = (x1, x2, f a, g b)
+
+instance Bifunctor ((,,,,) x1 x2 x3) where
+  bimap f g ~(x1, x2, x3, a, b) = (x1, x2, x3, f a, g b)
+
+instance Bifunctor ((,,,,,) x1 x2 x3 x4) where
+  bimap f g ~(x1, x2, x3, x4, a, b) = (x1, x2, x3, x4, f a, g b)
+
+instance Bifunctor ((,,,,,,) x1 x2 x3 x4 x5) where
+  bimap f g ~(x1, x2, x3, x4, x5, a, b) = (x1, x2, x3, x4, x5, f a, g b)
+
+instance Bifunctor Either where
+  bimap f _ (Left a) = Left (f a)
+  bimap _ g (Right b) = Right (g b)
+
+instance Bifunctor Const where
+  bimap f _ (Const a) = Const (f a)
diff --git a/src/Protolude/Bool.hs b/src/Protolude/Bool.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Bool.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Bool (
+  whenM
+, unlessM
+, ifM
+, guardM
+, bool
+, (&&^)
+, (||^)
+, (<&&>)
+, (<||>)
+) where
+
+import Data.Bool (Bool(..), (&&), (||))
+import Data.Function (flip)
+import Control.Applicative(Applicative, liftA2)
+import Control.Monad (Monad, MonadPlus, return, when, unless, guard, (>>=), (=<<))
+
+bool :: a -> a -> Bool -> a
+bool f t p = if p then t else f
+
+whenM :: Monad m => m Bool -> m () -> m ()
+whenM p m =
+  p >>= flip when m
+
+unlessM :: Monad m => m Bool -> m () -> m ()
+unlessM p m =
+  p >>= flip unless m
+
+ifM :: Monad m => m Bool -> m a -> m a -> m a
+ifM p x y = p >>= \b -> if b then x else y
+
+guardM :: MonadPlus m => m Bool -> m ()
+guardM f = guard =<< f
+
+-- | The '||' operator lifted to a monad. If the first
+--   argument evaluates to 'True' the second argument will not
+--   be evaluated.
+infixr 2 ||^ -- same as (||)
+(||^) :: Monad m => m Bool -> m Bool -> m Bool
+(||^) a b = ifM a (return True) b
+
+infixr 2 <||>
+-- | '||' lifted to an Applicative.
+-- Unlike '||^' the operator is __not__ short-circuiting.
+(<||>) :: Applicative a => a Bool -> a Bool -> a Bool
+(<||>) = liftA2 (||)
+{-# INLINE (<||>) #-}
+
+-- | The '&&' operator lifted to a monad. If the first
+--   argument evaluates to 'False' the second argument will not
+--   be evaluated.
+infixr 3 &&^ -- same as (&&)
+(&&^) :: Monad m => m Bool -> m Bool -> m Bool
+(&&^) a b = ifM a b (return False)
+
+infixr 3 <&&>
+-- | '&&' lifted to an Applicative.
+-- Unlike '&&^' the operator is __not__ short-circuiting.
+(<&&>) :: Applicative a => a Bool -> a Bool -> a Bool
+(<&&>) = liftA2 (&&)
+{-# INLINE (<&&>) #-}
diff --git a/src/Protolude/CallStack.hs b/src/Protolude/CallStack.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/CallStack.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+module Protolude.CallStack
+( HasCallStack
+) where
+
+#if MIN_VERSION_base(4,9,0)
+import GHC.Stack (HasCallStack)
+#elif MIN_VERSION_base(4,8,1)
+import qualified GHC.Stack
+type HasCallStack = (?callStack :: GHC.Stack.CallStack)
+#else
+import GHC.Exts (Constraint)
+type HasCallStack = (() :: Constraint)
+#endif
diff --git a/src/Protolude/Conv.hs b/src/Protolude/Conv.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Conv.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module Protolude.Conv (
+  StringConv (..)
+, toS
+, toSL
+, Leniency (..)
+) where
+
+import Data.ByteString.Char8      as B
+import Data.ByteString.Lazy.Char8 as LB
+import Data.Text                  as T
+import Data.Text.Encoding         as T
+import Data.Text.Encoding.Error   as T
+import Data.Text.Lazy             as LT
+import Data.Text.Lazy.Encoding    as LT
+
+import Protolude.Base
+import Data.Eq (Eq(..))
+import Data.Ord (Ord(..))
+import Data.Function ((.), id)
+import Data.String (String)
+import Control.Applicative (pure)
+
+data Leniency = Lenient | Strict
+  deriving (Eq,Show,Ord,Enum,Bounded)
+
+class StringConv a b where
+  strConv :: Leniency -> a -> b
+
+toS :: StringConv a b => a -> b
+toS = strConv Strict
+
+toSL :: StringConv a b => a -> b
+toSL = strConv Lenient
+
+instance StringConv String String where strConv _ = id
+instance StringConv String B.ByteString where strConv _ = B.pack
+instance StringConv String LB.ByteString where strConv _ = LB.pack
+instance StringConv String T.Text where strConv _ = T.pack
+instance StringConv String LT.Text where strConv _ = LT.pack
+
+instance StringConv B.ByteString String where strConv _ = B.unpack
+instance StringConv B.ByteString B.ByteString where strConv _ = id
+instance StringConv B.ByteString LB.ByteString where strConv _ = LB.fromChunks . pure
+instance StringConv B.ByteString T.Text where strConv = decodeUtf8T
+instance StringConv B.ByteString LT.Text where strConv l = strConv l . LB.fromChunks . pure
+
+instance StringConv LB.ByteString String where strConv _ = LB.unpack
+instance StringConv LB.ByteString B.ByteString where strConv _ = B.concat . LB.toChunks
+instance StringConv LB.ByteString LB.ByteString where strConv _ = id
+instance StringConv LB.ByteString T.Text where strConv l = decodeUtf8T l . strConv l
+instance StringConv LB.ByteString LT.Text where strConv = decodeUtf8LT
+
+instance StringConv T.Text String where strConv _ = T.unpack
+instance StringConv T.Text B.ByteString where strConv _ = T.encodeUtf8
+instance StringConv T.Text LB.ByteString where strConv l = strConv l . T.encodeUtf8
+instance StringConv T.Text LT.Text where strConv _ = LT.fromStrict
+instance StringConv T.Text T.Text where strConv _ = id
+
+instance StringConv LT.Text String where strConv _ = LT.unpack
+instance StringConv LT.Text T.Text where strConv _ = LT.toStrict
+instance StringConv LT.Text LT.Text where strConv _ = id
+instance StringConv LT.Text LB.ByteString where strConv _ = LT.encodeUtf8
+instance StringConv LT.Text B.ByteString where strConv l = strConv l . LT.encodeUtf8
+
+decodeUtf8T :: Leniency -> B.ByteString -> T.Text
+decodeUtf8T Lenient = T.decodeUtf8With T.lenientDecode
+decodeUtf8T Strict = T.decodeUtf8With T.strictDecode
+
+decodeUtf8LT :: Leniency -> LB.ByteString -> LT.Text
+decodeUtf8LT Lenient = LT.decodeUtf8With T.lenientDecode
+decodeUtf8LT Strict = LT.decodeUtf8With T.strictDecode
diff --git a/src/Protolude/Either.hs b/src/Protolude/Either.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Either.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Either (
+  maybeToLeft
+, maybeToRight
+, leftToMaybe
+, rightToMaybe
+, maybeEmpty
+, maybeToEither
+) where
+
+import Data.Function (const)
+import Data.Monoid (Monoid, mempty)
+import Data.Maybe (Maybe(..), maybe)
+import Data.Either (Either(..), either)
+
+leftToMaybe :: Either l r -> Maybe l
+leftToMaybe = either Just (const Nothing)
+
+rightToMaybe :: Either l r -> Maybe r
+rightToMaybe = either (const Nothing) Just
+
+maybeToRight :: l -> Maybe r -> Either l r
+maybeToRight l = maybe (Left l) Right
+
+maybeToLeft :: r -> Maybe l -> Either l r
+maybeToLeft r = maybe (Right r) Left
+
+maybeEmpty :: Monoid b => (a -> b) -> Maybe a -> b
+maybeEmpty = maybe mempty
+
+maybeToEither :: e -> Maybe a -> Either e a
+maybeToEither e Nothing = Left e
+maybeToEither _ (Just a) = Right a
diff --git a/src/Protolude/Error.hs b/src/Protolude/Error.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Error.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE ExistentialQuantification #-}
+#if ( __GLASGOW_HASKELL__ >= 800 )
+{-# LANGUAGE TypeInType #-}
+#endif
+
+#if MIN_VERSION_base(4,9,0)
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+#endif
+
+module Protolude.Error
+( error
+) where
+
+import GHC.Prim
+import Data.Text (Text, unpack)
+
+#if MIN_VERSION_base(4,9,0)
+-- Full stack trace.
+
+import GHC.Types (RuntimeRep)
+import Protolude.CallStack (HasCallStack)
+import GHC.Exception (errorCallWithCallStackException)
+
+{-# WARNING error "'error' remains in code" #-}
+error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack => Text -> a
+error s = raise# (errorCallWithCallStackException (unpack s) ?callstack)
+
+#elif MIN_VERSION_base(4,7,0)
+-- Basic Call Stack with callsite.
+
+import GHC.Exception (errorCallException)
+
+{-# WARNING error "'error' remains in code" #-}
+error :: Text -> a
+error s = raise# (errorCallException (unpack s))
+
+#else
+
+-- No exception tracing.
+import GHC.Types
+import GHC.Exception
+
+{-# WARNING error "'error' remains in code" #-}
+error :: Text -> a
+error s = throw (ErrorCall (unpack s))
+
+#endif
diff --git a/src/Protolude/Exceptions.hs b/src/Protolude/Exceptions.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Exceptions.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Exceptions (
+  hush,
+  note,
+  tryIO,
+) where
+
+import Protolude.Base (IO)
+import Data.Function ((.))
+import Control.Monad.Trans (liftIO)
+import Control.Monad.IO.Class (MonadIO)
+import Control.Monad.Except (ExceptT(..), MonadError, throwError)
+import Control.Exception as Exception
+import Control.Applicative
+import Data.Maybe (Maybe, maybe)
+import Data.Either (Either(..))
+
+hush :: Alternative m => Either e a -> m a
+hush (Left _)  = empty
+hush (Right x) = pure x
+
+-- To suppress redundant applicative constraint warning on GHC 8.0
+#if ( __GLASGOW_HASKELL__ >= 800 )
+note :: (MonadError e m) => e -> Maybe a -> m a
+note err = maybe (throwError err) pure
+#else
+note :: (MonadError e m, Applicative m) => e -> Maybe a -> m a
+note err = maybe (throwError err) pure
+#endif
+
+tryIO :: MonadIO m => IO a -> ExceptT IOException m a
+tryIO = ExceptT . liftIO . Exception.try
diff --git a/src/Protolude/Functor.hs b/src/Protolude/Functor.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Functor.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Functor (
+  Functor(..),
+  ($>),
+  (<$>),
+  (<<$>>),
+  void,
+) where
+
+import Data.Function ((.))
+
+#if MIN_VERSION_base(4,7,0)
+import Data.Functor (
+    Functor(..)
+  , ($>)
+  , (<$>)
+  , void
+  )
+#else
+import Data.Functor (
+    Functor(..)
+  , (<$>)
+  )
+
+import Data.Function (flip)
+
+infixl 4 $>
+
+($>) :: Functor f => f a -> b -> f b
+($>) = flip (<$)
+
+void :: Functor f => f a -> f ()
+void x = () <$ x
+#endif
+
+infixl 4 <<$>>
+
+(<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
+(<<$>>) = fmap . fmap
diff --git a/src/Protolude/List.hs b/src/Protolude/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/List.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.List (
+  head,
+  ordNub,
+  sortOn,
+  list,
+  product,
+  sum
+) where
+
+import Data.List (sortBy)
+import Data.Maybe (Maybe(..))
+import Data.Ord (Ord, comparing)
+import Data.Foldable (Foldable, foldr, foldl')
+import Data.Function ((.))
+import Data.Functor (fmap)
+import Control.Applicative (pure)
+import qualified Data.Set as Set
+import GHC.Num (Num, (+), (*))
+
+head :: (Foldable f) => f a -> Maybe a
+head = foldr (\x _ -> pure x) Nothing
+
+sortOn :: (Ord o) => (a -> o) -> [a] -> [a]
+sortOn = sortBy . comparing
+
+-- O(n * log n)
+ordNub :: (Ord a) => [a] -> [a]
+ordNub l = go Set.empty l
+  where
+    go _ []     = []
+    go s (x:xs) =
+      if x `Set.member` s
+      then go s xs
+      else x : go (Set.insert x s) xs
+
+list :: [b] -> (a -> b) -> [a] -> [b]
+list def f xs = case xs of
+  [] -> def
+  _  -> fmap f xs
+
+{-# INLINE product #-}
+product :: (Foldable f, Num a) => f a -> a
+product = foldl' (*) 1
+
+{-# INLINE sum #-}
+sum :: (Foldable f, Num a) => f a -> a
+sum = foldl' (+) 0
diff --git a/src/Protolude/Monad.hs b/src/Protolude/Monad.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Monad.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Monad (
+    Monad((>>=), return)
+  , MonadPlus(..)
+
+  , (=<<)
+  , (>=>)
+  , (<=<)
+  , (>>)
+  , forever
+
+  , join
+  , mfilter
+  , filterM
+  , mapAndUnzipM
+  , zipWithM
+  , zipWithM_
+  , foldM
+  , foldM_
+  , replicateM
+  , replicateM_
+  , concatMapM
+
+  , guard
+  , when
+  , unless
+
+  , liftM
+  , liftM2
+  , liftM3
+  , liftM4
+  , liftM5
+  , liftM'
+  , liftM2'
+  , ap
+
+  , (<$!>)
+  ) where
+
+import Protolude.Base (seq)
+import Data.List (concat)
+import Control.Monad
+
+concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
+concatMapM f xs = liftM concat (mapM f xs)
+
+liftM' :: Monad m => (a -> b) -> m a -> m b
+liftM' = (<$!>)
+{-# INLINE liftM' #-}
+
+liftM2' :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
+liftM2' f a b = do
+  x <- a
+  y <- b
+  let z = f x y
+  z `seq` return z
+{-# INLINE liftM2' #-}
+
+#if !MIN_VERSION_base(4,8,0)
+(<$!>) :: Monad m => (a -> b) -> m a -> m b
+f <$!> m = do
+  x <- m
+  let z = f x
+  z `seq` return z
+{-# INLINE (<$!>) #-}
+#endif
diff --git a/src/Protolude/Panic.hs b/src/Protolude/Panic.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Panic.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+#if MIN_VERSION_base(4,9,0)
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+#endif
+
+module Protolude.Panic (
+  FatalError(..),
+  panic,
+) where
+
+import Protolude.Base (Show)
+import Protolude.CallStack (HasCallStack)
+import Data.Text (Text)
+import Data.Typeable (Typeable)
+import Control.Exception as X
+
+-- | Uncatchable exceptions thrown and never caught.
+data FatalError = FatalError { fatalErrorMessage :: Text }
+  deriving (Show, Typeable)
+
+instance Exception FatalError
+
+panic :: HasCallStack => Text -> a
+panic a = throw (FatalError a)
diff --git a/src/Protolude/Semiring.hs b/src/Protolude/Semiring.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Semiring.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE Safe #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Protolude.Semiring (
+  Semiring(..),
+  zero,
+) where
+
+import Data.Monoid
+
+-- | Alias for 'mempty'
+zero :: Monoid m => m
+zero = mempty
+
+class Monoid m => Semiring m where
+  {-# MINIMAL one, (<.>) #-}
+
+  one :: m
+  (<.>) :: m -> m -> m
diff --git a/src/Protolude/Show.hs b/src/Protolude/Show.hs
new file mode 100644
--- /dev/null
+++ b/src/Protolude/Show.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE ExtendedDefaultRules #-}
+
+module Protolude.Show (
+  Print(..),
+  putText,
+  putErrText,
+  putLText,
+  putByteString,
+  putLByteString,
+) where
+
+import qualified System.IO as Base
+import qualified Protolude.Base as Base
+import Data.Function ((.))
+
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as BL
+
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.IO as TL
+
+import System.IO (Handle, stdout, stderr)
+
+
+class Print a where
+  hPutStr :: MonadIO m => Handle -> a -> m ()
+  putStr :: MonadIO m => a -> m ()
+  putStr = hPutStr stdout
+  hPutStrLn :: MonadIO m => Handle -> a -> m ()
+  putStrLn :: MonadIO m => a -> m ()
+  putStrLn = hPutStrLn stdout
+  putErrLn ::  MonadIO m => a -> m ()
+  putErrLn = hPutStrLn stderr
+
+instance Print T.Text where
+  hPutStr = \h -> liftIO . T.hPutStr h
+  hPutStrLn = \h -> liftIO . T.hPutStrLn h
+
+instance Print TL.Text where
+  hPutStr = \h -> liftIO . TL.hPutStr h
+  hPutStrLn = \h -> liftIO . TL.hPutStrLn h
+
+instance Print BS.ByteString where
+  hPutStr = \h -> liftIO . BS.hPutStr h
+  hPutStrLn = \h -> liftIO . BS.hPutStrLn h
+
+instance Print BL.ByteString where
+  hPutStr = \h -> liftIO . BL.hPutStr h
+  hPutStrLn = \h -> liftIO . BL.hPutStrLn h
+
+instance Print [Base.Char] where
+  hPutStr = \h -> liftIO . Base.hPutStr h
+  hPutStrLn = \h -> liftIO . Base.hPutStrLn h
+
+-- For forcing type inference
+putText :: MonadIO m => T.Text -> m ()
+putText = putStrLn
+{-# SPECIALIZE putText :: T.Text -> Base.IO () #-}
+
+putLText :: MonadIO m => TL.Text -> m ()
+putLText = putStrLn
+{-# SPECIALIZE putLText :: TL.Text -> Base.IO () #-}
+
+putByteString :: MonadIO m => BS.ByteString -> m ()
+putByteString = putStrLn
+{-# SPECIALIZE putByteString :: BS.ByteString -> Base.IO () #-}
+
+putLByteString :: MonadIO m => BL.ByteString -> m ()
+putLByteString = putStrLn
+{-# SPECIALIZE putLByteString :: BL.ByteString -> Base.IO () #-}
+
+putErrText :: MonadIO m => T.Text -> m ()
+putErrText = putErrLn
+{-# SPECIALIZE putErrText :: T.Text -> Base.IO () #-}
diff --git a/src/Semiring.hs b/src/Semiring.hs
deleted file mode 100644
--- a/src/Semiring.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-{-# LANGUAGE Safe #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
-module Semiring (
-  Semiring(..),
-  zero,
-) where
-
-import Data.Monoid
-
--- | Alias for 'mempty'
-zero :: Monoid m => m
-zero = mempty
-
-class Monoid m => Semiring m where
-  {-# MINIMAL one, (<.>) #-}
-
-  one :: m
-  (<.>) :: m -> m -> m
diff --git a/src/Show.hs b/src/Show.hs
deleted file mode 100644
--- a/src/Show.hs
+++ /dev/null
@@ -1,76 +0,0 @@
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE ExtendedDefaultRules #-}
-
-module Show (
-  Print(..),
-  putText,
-  putLText,
-  putByteString,
-  putLByteString,
-) where
-
-import qualified Base
-import qualified System.IO as Base
-import Data.Function ((.))
-
-import Control.Monad.IO.Class (MonadIO, liftIO)
-import qualified Data.ByteString.Char8 as BS
-import qualified Data.ByteString.Lazy.Char8 as BL
-
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
-
-import qualified Data.Text.Lazy as TL
-import qualified Data.Text.Lazy.IO as TL
-
-import System.IO (Handle, stdout)
-
-
-class Print a where
-  hPutStr :: MonadIO m => Handle -> a -> m ()
-  putStr :: MonadIO m => a -> m ()
-  putStr = hPutStr stdout
-  hPutStrLn :: MonadIO m => Handle -> a -> m ()
-  putStrLn :: MonadIO m => a -> m ()
-  putStrLn = hPutStrLn stdout
-
-instance Print T.Text where
-  hPutStr = \h -> liftIO . T.hPutStr h
-  hPutStrLn = \h -> liftIO . T.hPutStrLn h
-
-instance Print TL.Text where
-  hPutStr = \h -> liftIO . TL.hPutStr h
-  hPutStrLn = \h -> liftIO . TL.hPutStrLn h
-
-instance Print BS.ByteString where
-  hPutStr = \h -> liftIO . BS.hPutStr h
-  hPutStrLn = \h -> liftIO . BS.hPutStrLn h
-
-instance Print BL.ByteString where
-  hPutStr = \h -> liftIO . BL.hPutStr h
-  hPutStrLn = \h -> liftIO . BL.hPutStrLn h
-
-instance Print [Base.Char] where
-  hPutStr = \h -> liftIO . Base.hPutStr h
-  hPutStrLn = \h -> liftIO . Base.hPutStrLn h
-
--- For forcing type inference
-putText :: MonadIO m => T.Text -> m ()
-putText = putStrLn
-{-# SPECIALIZE putText :: T.Text -> Base.IO () #-}
-
-putLText :: MonadIO m => TL.Text -> m ()
-putLText = putStrLn
-{-# SPECIALIZE putLText :: TL.Text -> Base.IO () #-}
-
-putByteString :: MonadIO m => BS.ByteString -> m ()
-putByteString = putStrLn
-{-# SPECIALIZE putByteString :: BS.ByteString -> Base.IO () #-}
-
-putLByteString :: MonadIO m => BL.ByteString -> m ()
-putLByteString = putStrLn
-{-# SPECIALIZE putLByteString :: BL.ByteString -> Base.IO () #-}
diff --git a/src/Unsafe.hs b/src/Unsafe.hs
--- a/src/Unsafe.hs
+++ b/src/Unsafe.hs
@@ -11,7 +11,7 @@
   unsafeThrow,
 ) where
 
-import Base (Int)
+import Protolude.Base (Int)
 import qualified Data.List as List
 import qualified Data.Maybe as Maybe
 import qualified Control.Exception as Exc
