diff --git a/intro.cabal b/intro.cabal
--- a/intro.cabal
+++ b/intro.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           intro
-version:        0.0.2.0
+version:        0.0.2.1
 synopsis:       Total Prelude with Text and Monad transformers
 description:    Modern Prelude which provides safe alternatives for most of the partial functions. Text is preferred over String. Container types and Monad transformers are provided. Most important - this Prelude avoids fanciness. This means it just reexports from base and commonly used libraries and doesn\'t invent its own stuff. Everything is in one file.
 category:       Prelude
diff --git a/src/Intro.hs b/src/Intro.hs
--- a/src/Intro.hs
+++ b/src/Intro.hs
@@ -1,5 +1,23 @@
 {-# LANGUAGE Safe #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Intro
+-- Copyright   :  (c) Daniel Mendler 2016
+-- License     :  MIT
+--
+-- Maintainer  :  mail@daniel-mendler.de
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Modern Prelude which provides safe alternatives for most of the partial functions. Text is preferred over String.
+-- Container types and Monad transformers are provided. Most important - this Prelude avoids fanciness.
+-- This means it just reexports from base and commonly used libraries and doesn\'t invent its own stuff. Everything is in one file.
+--
+-----------------------------------------------------------------------------
+
 module Intro (
   -- * Basic functions
   -- Data.Function.id
@@ -127,30 +145,38 @@
   , Data.Either.Extra.eitherToMaybe
   , Data.Either.Extra.maybeToEither
 
-  -- * Char, String and Text
+  -- * Text types
+
+  -- ** Char and String
   , Prelude.Char
   , Prelude.String
+
+  -- ** Text
   , Data.Text.Text
   , LText
 --  Data.Text.lines, -- Use qualified import instead
 --  Data.Text.words,
 --  Data.Text.unlines,
 --  Data.Text.unwords,
+
+  -- ** ByteString
   , Data.ByteString.ByteString
   , LByteString
+
+  -- ** Conversion
   , Data.String.IsString(fromString)
   , Data.String.Conversions.ConvertibleStrings(convertString)
 
   -- * Container types
 
-  -- ** Ordered Map and Set
+  -- ** Map and Set (Ordered)
   , Data.Map.Strict.Map
   , LMap
   , Data.Set.Set
   , Data.IntMap.Strict.IntMap
   , Data.IntSet.IntSet
 
-  -- ** Hashed Map and Set
+  -- ** HashedMap and HashSet
   , Data.HashMap.Strict.HashMap
   , LHashMap
   , Data.HashSet.HashSet
@@ -247,7 +273,7 @@
   , Data.Functor.Classes.Read2
   , readMaybe
 
-  -- * Eq, Ord, etc.
+  -- * Equality and Ordering
 
   -- ** Eq
   , Data.Eq.Eq((==), (/=))
@@ -373,14 +399,14 @@
   , Data.Foldable.find
   , Data.Foldable.notElem
   , Data.Foldable.sequenceA_
-  , Safe.maximumByMay
-  , Safe.maximumByDef
-  , Safe.minimumByMay
-  , Safe.minimumByDef
-  , Safe.maximumMay
-  , Safe.maximumDef
-  , Safe.minimumMay
-  , Safe.minimumDef
+  , Safe.Foldable.maximumByMay
+  , Safe.Foldable.maximumByDef
+  , Safe.Foldable.minimumByMay
+  , Safe.Foldable.minimumByDef
+  , Safe.Foldable.maximumMay
+  , Safe.Foldable.maximumDef
+  , Safe.Foldable.minimumMay
+  , Safe.Foldable.minimumDef
 
   -- ** Traversable
   , Data.Traversable.Traversable(traverse, sequenceA)
@@ -478,7 +504,7 @@
   , Control.DeepSeq.NFData
   , Data.Binary.Binary
 
-  -- * Type level stuff
+  -- * Type level
   , Data.Kind.Type
   , Data.Kind.Constraint
   , Data.Proxy.Proxy(Proxy)
@@ -517,10 +543,11 @@
 
 import Control.Category ((.))
 import Control.Monad.Trans (MonadIO(liftIO))
+import Data.ByteString (ByteString)
 import Data.Semigroup ((<>))
 import Data.String.Conversions (ConvertibleStrings(convertString))
 import Data.Text (Text)
-import Prelude (String, Char, FilePath)
+import Prelude (String, Char, FilePath, Show)
 import qualified Control.Applicative
 import qualified Control.Category
 import qualified Control.DeepSeq
@@ -548,6 +575,7 @@
 import qualified Data.Foldable
 import qualified Data.Function
 import qualified Data.Functor
+import qualified Data.Functor.Classes
 import qualified Data.Functor.Const
 import qualified Data.Functor.Identity
 import qualified Data.HashMap.Lazy
@@ -586,10 +614,10 @@
 import qualified Numeric.Natural
 import qualified Prelude
 import qualified Safe
+import qualified Safe.Foldable
 import qualified System.IO
 import qualified Text.Read
 import qualified Text.Show
-import qualified Data.Functor.Classes
 
 -- | Alias for lazy 'Data.Text.Lazy.Text'
 type LText = Data.Text.Lazy.Text
@@ -603,69 +631,125 @@
 -- | Alias for lazy 'Data.HashMap.Lazy.HashMap'
 type LHashMap = Data.HashMap.Lazy.HashMap
 
--- | Map over a 'Data.Functor.Functor' type
+-- | A synonym for 'Data.Functor.fmap'.
+--
+--   @map = 'Data.Functor.fmap'@
 map :: Data.Functor.Functor f => (a -> b) -> f a -> f b
 map = Data.Functor.fmap
 {-# INLINE map #-}
 
-show :: (Text.Show.Show a, ConvertibleStrings String b) => a -> b
+-- | Convert a value to a readable string type supported by 'ConvertibleStrings' using the 'Show' instance.
+show :: (Show a, ConvertibleStrings String b) => a -> b
 show = convertString . showS
 {-# INLINE show #-}
 
-showS :: Text.Show.Show a => a -> String
+-- | Convert a value to a readable 'String' using the 'Show' instance.
+showS :: Show a => a -> String
 showS = Text.Show.show
 {-# INLINE showS #-}
 
+-- | Parse a string type using the 'Text.Read.Read' instance.
+-- Succeeds if there is exactly one valid result.
 readMaybe :: (Text.Read.Read b, ConvertibleStrings a String) => a -> Data.Maybe.Maybe b
 readMaybe = Text.Read.readMaybe . convertString
 {-# INLINE readMaybe #-}
 
-print :: (MonadIO m, Text.Show.Show a) => a -> m ()
+-- | The 'print' function outputs a value of any printable type to the
+-- standard output device.
+-- Printable types are those that are instances of class 'Show'; 'print'
+-- converts values to strings for output using the 'show' operation and
+-- adds a newline.
+--
+-- For example, a program to print the first 20 integers and their
+-- powers of 2 could be written as:
+--
+-- > main = print ([(n, 2^n) | n <- [0..19]])
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+print :: (MonadIO m, Show a) => a -> m ()
 print = liftIO . System.IO.print
 {-# INLINE print #-}
 
+-- | The 'getContents' operation returns all user input as a strict 'Text'.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 getContents :: MonadIO m => m Text
 getContents = liftIO Data.Text.IO.getContents
 {-# INLINE getContents #-}
 
+-- | Read a line from the standard input device as a strict 'Text'.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 getLine :: MonadIO m => m Text
 getLine = liftIO Data.Text.IO.getLine
 {-# INLINE getLine #-}
 
+-- | Read a character from the standard input device.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 getChar :: MonadIO m => m Char
 getChar = liftIO System.IO.getChar
 {-# INLINE getChar #-}
 
-putStr, putStrLn :: MonadIO m => Text -> m ()
+-- | Write a strict 'Text' to the standard output device.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+putStr :: MonadIO m => Text -> m ()
 putStr = liftIO . Data.Text.IO.putStr
-putStrLn = liftIO . Data.Text.IO.putStrLn
 {-# INLINE putStr #-}
+
+-- | The same as 'putStr', but adds a newline character.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+putStrLn :: MonadIO m => Text -> m ()
+putStrLn = liftIO . Data.Text.IO.putStrLn
 {-# INLINE putStrLn #-}
 
+-- | Write a character to the standard output device.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 putChar :: MonadIO m => Char -> m ()
 putChar = liftIO . System.IO.putChar
 {-# INLINE putChar #-}
 
-readFile :: MonadIO m => FilePath -> m Data.ByteString.ByteString
+-- | Read an entire file strictly into a 'ByteString'.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+readFile :: MonadIO m => FilePath -> m ByteString
 readFile = liftIO . Data.ByteString.readFile
 {-# INLINE readFile #-}
 
-writeFile :: MonadIO m => FilePath -> Data.ByteString.ByteString -> m ()
+-- | Write a 'ByteString' to a file.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+writeFile :: MonadIO m => FilePath -> ByteString -> m ()
 writeFile = liftIO .: Data.ByteString.writeFile
 {-# INLINE writeFile #-}
 
-appendFile :: MonadIO m => FilePath -> Data.ByteString.ByteString -> m ()
+-- | Append a 'ByteString' to a file.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
+appendFile :: MonadIO m => FilePath -> ByteString -> m ()
 appendFile = liftIO .: Data.ByteString.appendFile
 {-# INLINE appendFile #-}
 
+-- | Read an entire file strictly into a 'Text' using UTF-8 encoding.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 readFileUtf8 :: MonadIO m => FilePath -> m Text
 readFileUtf8 = map convertString . readFile
 {-# INLINE readFileUtf8 #-}
 
+-- | Write a 'Text' to a file using UTF-8 encoding.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 writeFileUtf8 :: MonadIO m => FilePath -> Text -> m ()
 writeFileUtf8 file = writeFile file . convertString
 {-# INLINE writeFileUtf8 #-}
 
+-- | Append a 'Text' to a file using UTF-8 encoding.
+--
+-- __Note__: This function is lifted to the 'MonadIO' class.
 appendFileUtf8 :: MonadIO m => FilePath -> Text -> m ()
 appendFileUtf8 file = appendFile file . convertString
 {-# INLINE appendFileUtf8 #-}
@@ -681,18 +765,27 @@
 infixr 6 <>^
 {-# INLINE (<>^) #-}
 
--- | Compose functions with one argument with function with two arguments
+-- | Compose functions with one argument with function with two arguments.
+--
+--   @f .: g = \\x y -> f (g x y)@.
 (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
 (.:) = (.) . (.)
 infixr 8 .:
 {-# INLINE (.:) #-}
 
--- | Shortcut for 'pure ()'
+-- | '()' lifted to an 'Control.Applicative.Applicative'.
+--
+--   @skip = 'Control.Applicative.pure' ()@
 skip :: Control.Applicative.Applicative m => m ()
 skip = Control.Applicative.pure ()
 {-# INLINE skip #-}
 
--- | Throw an unhandled error. Use this function instead of 'Prelude.error'
+-- | Throw an unhandled error to terminate the program in case
+-- of a logic error at runtime. Use this function instead of 'Prelude.error'.
+-- A stack trace will be provided.
+--
+-- In general, prefer total functions. You can use 'Data.Maybe.Maybe', 'Data.Either.Either',
+-- 'Control.Monad.Except.ExceptT' or 'Control.Monad.Except.MonadError' for error handling.
 panic :: GHC.Stack.Types.HasCallStack => a
 panic = Prelude.error Data.Function.$
   "Panic!\n" <>
diff --git a/src/Intro/Trustworthy.hs b/src/Intro/Trustworthy.hs
--- a/src/Intro/Trustworthy.hs
+++ b/src/Intro/Trustworthy.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE NoImplicitPrelude #-}
 module Intro.Trustworthy (
   Data.DList.DList
   , GHC.Exts.IsList(
@@ -15,42 +16,96 @@
   , traceStackM
 ) where
 
+import Control.Applicative (Applicative(pure))
+import Control.Monad.Trans (MonadIO(liftIO))
+import Data.Function ((.), ($))
+import Data.Text (Text)
+import Text.Show (Show)
 import qualified Data.DList
-import qualified GHC.Exts
 import qualified Data.Text
 import qualified Debug.Trace
+import qualified GHC.Exts
 
--- | See 'Debug.trace.trace'
-trace :: Data.Text.Text -> a -> a
+-- | The 'trace' function outputs the trace message given as its first argument,
+-- before returning the second argument as its result.
+--
+-- For example, this returns the value of @f x@ but first outputs the message.
+--
+-- > trace ("calling f with x = " ++ show x) (f x)
+--
+-- The 'trace' function should /only/ be used for debugging, or for monitoring
+-- execution. The function is not referentially transparent: its type indicates
+-- that it is a pure function but it has the side effect of outputting the
+-- trace message.
+trace :: Text -> a -> a
 trace = Debug.Trace.trace . Data.Text.unpack
 {-# WARNING trace "'trace' remains in code" #-}
 
--- | See 'Debug.trace.traceStack'
-traceStack :: Data.Text.Text -> a -> a
+-- | Like 'trace' but returning unit in an arbitrary 'Applicative' context. Allows
+-- for convenient use in do-notation.
+--
+-- Note that the application of 'traceM' is not an action in the 'Applicative'
+-- context, as 'traceIO' is in the 'MonadIO' type. While the fresh bindings in the
+-- following example will force the 'traceM' expressions to be reduced every time
+-- the @do@-block is executed, @traceM "not crashed"@ would only be reduced once,
+-- and the message would only be printed once.  If your monad is in 'MonadIO',
+-- @traceIO@ may be a better option.
+--
+-- > ... = do
+-- >   x <- ...
+-- >   traceM $ "x: " ++ show x
+-- >   y <- ...
+-- >   traceM $ "y: " ++ show y
+traceM :: Applicative m => Text -> m ()
+traceM = Debug.Trace.traceM . Data.Text.unpack
+{-# WARNING traceM "'traceM' remains in code" #-}
+
+-- | like 'trace', but additionally prints a call stack if one is
+-- available.
+--
+-- In the current GHC implementation, the call stack is only
+-- available if the program was compiled with @-prof@; otherwise
+-- 'traceStack' behaves exactly like 'trace'.  Entries in the call
+-- stack correspond to @SCC@ annotations, so it is a good idea to use
+-- @-fprof-auto@ or @-fprof-auto-calls@ to add SCC annotations automatically.
+traceStack :: Text -> a -> a
 traceStack = Debug.Trace.traceStack . Data.Text.unpack
 {-# WARNING traceStack "'traceStack' remains in code" #-}
 
--- | 'traceStack' lifted to a 'Monad'
-traceStackM :: Monad m => Data.Text.Text -> m ()
+-- | Like 'traceStack' but returning unit in an arbitrary 'Applicative' context. Allows
+-- for convenient use in do-notation.
+traceStackM :: Applicative m => Text -> m ()
 traceStackM s = traceStack s $ pure ()
 {-# WARNING traceStackM "'traceStackM' remains in code" #-}
 
--- | See 'Debug.Trace.traceShow'
+-- | Like 'trace', but uses 'show' on the argument to convert it to a 'String'.
+--
+-- This makes it convenient for printing the values of interesting variables or
+-- expressions inside a function. For example here we print the value of the
+-- variables @x@ and @z@:
+--
+-- > f x y =
+-- >     traceShow (x, z) $ result
+-- >   where
+-- >     z = ...
+-- >     ...
 traceShow :: Show a => a -> b -> b
 traceShow = Debug.Trace.traceShow
 {-# WARNING traceShow "'traceShow' remains in code" #-}
 
--- | See 'Debug.Trace.traceShowM'
-traceShowM :: (Show a, Monad m) => a -> m ()
+-- | Like 'traceM', but uses 'show' on the argument to convert it to a 'String'.
+--
+-- > ... = do
+-- >   x <- ...
+-- >   traceShowM $ x
+-- >   y <- ...
+-- >   traceShowM $ x + y
+traceShowM :: (Show a, Applicative m) => a -> m ()
 traceShowM = Debug.Trace.traceShowM
 {-# WARNING traceShowM "'traceShowM' remains in code" #-}
 
--- | See 'Debug.Trace.traceM'
-traceM :: Monad m => Data.Text.Text -> m ()
-traceM = Debug.Trace.traceM . Data.Text.unpack
-{-# WARNING traceM "'traceM' remains in code" #-}
-
--- | See 'Debug.Trace.traceIO'
-traceIO :: Data.Text.Text -> IO ()
-traceIO = Debug.Trace.traceIO . Data.Text.unpack
+-- | The 'traceIO' function outputs the trace message from the IO monad.
+-- This sequences the output with respect to other IO actions.
+traceIO :: MonadIO m => Text -> m ()
+traceIO = liftIO . Debug.Trace.traceIO . Data.Text.unpack
 {-# WARNING traceIO "'traceIO' remains in code" #-}
