diff --git a/esqueleto.cabal b/esqueleto.cabal
--- a/esqueleto.cabal
+++ b/esqueleto.cabal
@@ -1,5 +1,5 @@
 name:                esqueleto
-version:             0.2.1
+version:             0.2.2
 synopsis:            Bare bones, type-safe EDSL for SQL queries on persistent backends.
 homepage:            https://github.com/meteficha/esqueleto
 license:             BSD3
@@ -42,7 +42,8 @@
   <https://github.com/meteficha/esqueleto/>.
   .
   The name of this library means \"skeleton\" in Portuguese and
-  contains all three SQL letters in the correct order =).
+  contains all three SQL letters in the correct order =).  It was
+  inspired by Scala's Squeryl but created from scratch.
 
 source-repository head
   type:     git
diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs
--- a/src/Database/Esqueleto/Internal/Language.hs
+++ b/src/Database/Esqueleto/Internal/Language.hs
@@ -7,8 +7,12 @@
            , TypeFamilies
            , UndecidableInstances
  #-}
+-- | This is an internal module, anything exported by this module
+-- may change without a major version bump.  Please use only
+-- "Database.Esqueleto" if possible.
 module Database.Esqueleto.Internal.Language
-  ( Esqueleto(..)
+  ( -- * The pretty face
+    Esqueleto(..)
   , from
   , Value(..)
   , InnerJoin(..)
@@ -16,12 +20,15 @@
   , LeftOuterJoin(..)
   , RightOuterJoin(..)
   , FullOuterJoin(..)
-  , JoinKind(..)
-  , IsJoinKind(..)
   , OnClauseWithoutMatchingJoinException(..)
-  , PreprocessedFrom
   , OrderBy
   , Update
+    -- * The guts
+  , JoinKind(..)
+  , IsJoinKind(..)
+  , PreprocessedFrom
+  , From
+  , FromPreprocess
   ) where
 
 import Control.Applicative (Applicative(..), (<$>))
@@ -82,7 +89,7 @@
   --
   -- @
   -- select $
-  -- from $ \(foo `InnerJoin` bar) -> do
+  -- from $ \\(foo ``InnerJoin`` bar) -> do
   --   on (foo ^. FooId ==. bar ^. BarFooId)
   --   ...
   -- @
@@ -94,7 +101,7 @@
   --
   -- @
   -- select $
-  -- from $ \(foo `InnerJoin` bar `InnerJoin` baz) -> do
+  -- from $ \\(foo ``InnerJoin`` bar ``InnerJoin`` baz) -> do
   --   on (baz ^. BazId ==. bar ^. BarBazId)
   --   on (foo ^. FooId ==. bar ^. BarFooId)
   --   ...
@@ -105,18 +112,16 @@
   --
   -- @
   -- let query1 =
-  --       from $ \(foo `InnerJoin` bar) -> do
+  --       from $ \\(foo ``InnerJoin`` bar) -> do
   --         on (foo ^. FooId ==. bar ^. BarFooId)
-  --
   --     query2 =
-  --       from $ \(mbaz `LeftOuterJoin` quux) -> do
+  --       from $ \\(mbaz ``LeftOuterJoin`` quux) -> do
   --         return (mbaz ?. BazName, quux)
-  --
-  --     test1 =      (,) <$> query1 <*> query2
-  --     test2 = flip (,) <$> query2 <*> query1
+  --     test1 =      (,) \<$\> query1 \<*\> query2
+  --     test2 = flip (,) \<$\> query2 \<*\> query1
   -- @
   --
-  -- If the order was *not* reversed, then @test2@ would be
+  -- If the order was /not/ reversed, then @test2@ would be
   -- broken: @query1@'s 'on' would refer to @query2@'s
   -- 'LeftOuterJoin'.
   on :: expr (Value Bool) -> query ()
@@ -150,9 +155,9 @@
   -- | @IS NULL@ comparison.
   isNothing :: PersistField typ => expr (Value (Maybe typ)) -> expr (Value Bool)
 
-  -- | Analog to 'Just', promotes a value of type @typ@ into one
-  -- of type @Maybe typ@.  It should hold that @val . Just ===
-  -- just . val@.
+  -- | Analogous to 'Just', promotes a value of type @typ@ into
+  -- one of type @Maybe typ@.  It should hold that @val . Just
+  -- === just . val@.
   just :: expr (Value typ) -> expr (Value (Maybe typ))
 
   -- | @NULL@ value.
@@ -215,7 +220,7 @@
 --
 -- @
 -- select $
--- from $ \(person `LeftOuterJoin` pet) ->
+-- from $ \\(person ``LeftOuterJoin`` pet) ->
 --   ...
 -- @
 --
@@ -226,6 +231,8 @@
 -- FROM Person LEFT OUTER JOIN Pet
 -- ...
 -- @
+--
+-- See also: 'from'.
 data LeftOuterJoin a b = a `LeftOuterJoin` b
 
 -- | Data type that represents a @RIGHT OUTER JOIN@ (see 'LeftOuterJoin' for an example).
@@ -290,19 +297,75 @@
 data Update typ
 
 
--- | @FROM@ clause: bring an entity into scope.
+-- | @FROM@ clause: bring entities into scope.
 --
--- The following types implement 'from':
+-- This function internally uses two type classes in order to
+-- provide some flexibility of how you may call it.  Internally
+-- we refer to these type classes as the two different magics.
 --
---  * @Expr (Entity val)@, which brings a single entity into scope.
+-- The innermost magic allows you to use @from@ with the
+-- following types:
 --
---  * Tuples of any other types supported by 'from'.  Calling
---  'from' multiple times is the same as calling 'from' a
---  single time and using a tuple.
+--  * @expr (Entity val)@, which brings a single entity into
+--  scope.
 --
--- Note that using 'from' for the same entity twice does work
--- and corresponds to a self-join.  You don't even need to use
--- two different calls to 'from', you may use a tuple.
+--  * @expr (Maybe (Entity val))@, which brings a single entity
+--  that may be @NULL@ into scope.  Used for @OUTER JOIN@s.
+--
+--  * A @JOIN@ of any other two types allowed by the innermost
+--  magic, where a @JOIN@ may be an 'InnerJoin', a 'CrossJoin', a
+--  'LeftOuterJoin', a 'RightOuterJoin', or a 'FullOuterJoin'.
+--  The @JOINs@ have right fixity, the same as in SQL.
+--
+-- The outermost magic allows you to use @from@ on any tuples of
+-- types supported by innermost magic (and also tuples of tuples,
+-- and so on), up to 8-tuples.
+--
+-- Note that using @from@ for the same entity twice does work and
+-- corresponds to a self-join.  You don't even need to use two
+-- different calls to @from@, you may use a @JOIN@ or a tuple.
+--
+-- The following are valid examples of uses of @from@ (the types
+-- of the arguments of the lambda are inside square brackets):
+--
+-- @
+-- from $ \\person -> ...
+-- from $ \\(person, blogPost) -> ...
+-- from $ \\(p ``LeftOuterJoin`` mb) -> ...
+-- from $ \\(p1 ``InnerJoin`` f ``InnerJoin`` p2) -> ...
+-- from $ \\((p1 ``InnerJoin`` f) ``InnerJoin`` p2) -> ...
+-- @
+--
+-- The types of the arguments to the lambdas above are,
+-- respectively:
+--
+-- @
+-- person
+--   :: ( Esqueleto query expr backend
+--      , PersistEntity Person
+--      , PersistEntityBackend Person ~ backend
+--      ) => expr (Entity Person)
+-- (person, blogPost)
+--   :: (...) => (expr (Entity Person), expr (Entity BlogPost))
+-- (p ``LeftOuterJoin`` mb)
+--   :: (...) => InnerJoin (expr (Entity Person)) (expr (Maybe (Entity BlogPost)))
+-- (p1 ``InnerJoin`` f ``InnerJoin`` p2)
+--   :: (...) => InnerJoin
+--                 (expr (Entity Person))
+--                 (InnerJoin (expr (Entity Follow))
+--                            (expr (Entity Person)))
+-- ((p1 ``InnerJoin`` f) ``InnerJoin`` p2) ::
+--   :: (...) => InnerJoin
+--                 (InnerJoin (expr (Entity Person))
+--                            (expr (Entity Follow)))
+--                 (expr (Entity Person))
+-- @
+--
+-- Note that some backends may not support all kinds of @JOIN@s.
+-- For example, when using the SQL backend with SQLite, it will
+-- not accept the last example above (which is associated to the
+-- left, instead of being to the right) and will not accept
+-- 'RightOuterJoin's or 'FullOuterJoin's.
 from :: From query expr backend a => (a -> query b) -> query b
 from = (from_ >>=)
 
diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs
--- a/src/Database/Esqueleto/Internal/Sql.hs
+++ b/src/Database/Esqueleto/Internal/Sql.hs
@@ -7,20 +7,27 @@
            , OverloadedStrings
            , UndecidableInstances
  #-}
+-- | This is an internal module, anything exported by this module
+-- may change without a major version bump.  Please use only
+-- "Database.Esqueleto" if possible.
 module Database.Esqueleto.Internal.Sql
-  ( SqlQuery
+  ( -- * The pretty face
+    SqlQuery
   , SqlExpr
   , select
   , selectSource
   , selectDistinct
   , selectDistinctSource
+  , delete
+  , update
+    -- * The guts
   , rawSelectSource
   , runSource
   , rawExecute
-  , delete
-  , update
   , toRawSql
   , Mode(..)
+  , Escape
+  , SqlSelect
   ) where
 
 import Control.Applicative (Applicative(..), (<$>))
@@ -199,6 +206,7 @@
 
 data OrderByType = ASC | DESC
 
+-- | (Internal) Backend-specific function that escapes a 'DBName'.
 type Escape = DBName -> TLB.Builder
 
 
@@ -356,6 +364,45 @@
 
 -- | Execute an @esqueleto@ @SELECT@ query inside @persistent@'s
 -- 'SqlPersist' monad and return a list of rows.
+--
+-- We've seen that 'from' has some magic about which kinds of
+-- things you may bring into scope.  This 'select' function also
+-- has some magic for which kinds of things you may bring back to
+-- Haskell-land by using @SqlQuery@'s @return@:
+--
+--  * You may return a @SqlExpr ('Entity' v)@ for an entity @v@
+--  (i.e., like the @*@ in SQL), which is then returned to
+--  Haskell-land as just @Entity v@.
+--
+--  * You may return a @SqlExpr (Maybe (Entity v))@ for an entity
+--  @v@ that may be @NULL@, which is then returned to
+--  Haskell-land as @Maybe (Entity v)@.  Used for @OUTER JOIN@s.
+--
+--  * You may return a @SqlExpr ('Value' t)@ for a value @t@
+--  (i.e., a single column), where @t@ is any instance of
+--  'PersistField', which is then returned to Haskell-land as
+--  @Value t@.  You may use @Value@ to return projections of an
+--  @Entity@ (see @('^.')@ and @('?.')@) or to return any other
+--  value calculated on the query (e.g., 'countRows' or
+--  'sub_select').
+--
+-- The @SqlSelect a r@ class has functional dependencies that
+-- allow type information to flow both from @a@ to @r@ and
+-- vice-versa.  This means that you'll almost never have to give
+-- any type signatures for @esqueleto@ queries.  For example, the
+-- query @select $ from $ \\p -> return p@ alone is ambiguous, but
+-- in the context of
+--
+-- @
+-- do ps <- select $
+--          from $ \\p ->
+--          return p
+--    liftIO $ mapM_ (putStrLn . personName . entityVal) ps
+-- @
+--
+-- we are able to infer from that single @personName . entityVal@
+-- function composition that the @p@ inside the query is of type
+-- @SqlExpr (Entity Person)@.
 select :: ( SqlSelect a r
           , MonadLogger m
           , MonadResourceBase m )
@@ -384,7 +431,7 @@
 selectDistinct = selectDistinctSource >=> runSource
 
 
--- | Runs a 'C.Source' of rows.
+-- | (Internal) Run a 'C.Source' of rows.
 runSource :: MonadResourceBase m =>
              C.Source (C.ResourceT (SqlPersist m)) r
           -> SqlPersist m [r]
@@ -420,6 +467,16 @@
 -- from $ \\appointment ->
 -- where_ (appointment ^. AppointmentDate <. val now)
 -- @
+--
+-- Unlike 'select', there is a useful way of using 'delete' that
+-- will lead to type ambiguities.  If you want to delete all rows
+-- (i.e., no 'where_' clause), you'll have to use a type signature:
+--
+-- @
+-- delete $
+-- from $ \\(appointment :: SqlExpr (Entity Appointment)) ->
+-- return ()
+-- @
 delete :: ( MonadLogger m
           , MonadResourceBase m )
        => SqlQuery ()
@@ -451,7 +508,12 @@
 ----------------------------------------------------------------------
 
 
--- | Pretty prints a 'SqlQuery' into a SQL query.
+-- | (Internal) Pretty prints a 'SqlQuery' into a SQL query.
+--
+-- Note: if you're curious about the SQL query being generated by
+-- @esqueleto@, instead of manually using this function (which is
+-- possible but tedious), you may just turn on query logging of
+-- @persistent@.
 toRawSql :: SqlSelect a r => Mode -> Escape -> SqlQuery a -> (TLB.Builder, [PersistValue])
 toRawSql mode esc query =
   let (ret, SideData fromClauses setClauses whereClauses orderByClauses) =
@@ -466,6 +528,7 @@
       , makeOrderBy esc orderByClauses
       ]
 
+-- | (Internal) Mode of query being converted by 'toRawSql'.
 data Mode = SELECT | SELECT_DISTINCT | DELETE | UPDATE
 
 
@@ -552,8 +615,11 @@
 parens b = "(" <> (b <> ")")
 
 
--- | Class for mapping results coming from 'SqlQuery' into actual
--- results.
+----------------------------------------------------------------------
+
+
+-- | (Internal) Class for mapping results coming from 'SqlQuery'
+-- into actual results.
 --
 -- This looks very similar to @RawSql@, and it is!  However,
 -- there are some crucial differences and ultimately they're
@@ -571,11 +637,15 @@
   -- | Transform a row of the result into the data type.
   sqlSelectProcessRow :: [PersistValue] -> Either T.Text r
 
+
+-- | Not useful for 'select', but used for 'update' and 'delete'.
 instance SqlSelect () () where
   sqlSelectCols _ _ = mempty
   sqlSelectColCount _ = 0
   sqlSelectProcessRow _ = Right ()
 
+
+-- | You may return an 'Entity' from a 'select' query.
 instance PersistEntity a => SqlSelect (SqlExpr (Entity a)) (Entity a) where
   sqlSelectCols escape expr@(EEntity ident) = ret
       where
@@ -602,6 +672,8 @@
 getEntityVal :: SqlExpr (Entity a) -> a
 getEntityVal = error "Esqueleto/Sql/getEntityVal"
 
+
+-- | You may return a possibly-@NULL@ 'Entity' from a 'select' query.
 instance PersistEntity a => SqlSelect (SqlExpr (Maybe (Entity a))) (Maybe (Entity a)) where
   sqlSelectCols escape (EMaybe ent) = sqlSelectCols escape ent
   sqlSelectColCount = sqlSelectColCount . fromEMaybe
@@ -612,6 +684,9 @@
     | all (== PersistNull) cols = return Nothing
     | otherwise                 = Just <$> sqlSelectProcessRow cols
 
+
+-- | You may return any single value (i.e. a single column) from
+-- a 'select' query.
 instance PersistField a => SqlSelect (SqlExpr (Value a)) (Value a) where
   sqlSelectCols esc (ERaw p f) = let (b, vals) = f esc
                                  in (parensM p b, vals)
@@ -619,6 +694,9 @@
   sqlSelectProcessRow [pv] = Value <$> fromPersistValue pv
   sqlSelectProcessRow _    = Left "SqlSelect (Value a): wrong number of columns."
 
+
+-- | You may return tuples (up to 8-tuples) and tuples of tuples
+-- from a 'select' query.
 instance ( SqlSelect a ra
          , SqlSelect b rb
          ) => SqlSelect (a, b) (ra, rb) where
@@ -642,6 +720,7 @@
 
     in colCountFst `seq` processRow
        -- Avoids recalculating 'colCountFst'.
+
 
 instance ( SqlSelect a ra
          , SqlSelect b rb
