diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@
             - [Limited `CREATE TABLE` support.](#limited-create-table-support)
             - [`INSERT INTO` support.](#insert-into-support)
             - [`DELETE FROM` support.](#delete-from-support)
+            - [`UPDATE TABLE` support.](#update-table-support)
     - [Roadmap](#roadmap)
-        - [UPDATE support](#update-support)
         - [Flesh out Haskell to PostgreSQL type mapping.](#flesh-out-haskell-to-postgresql-type-mapping)
     - [How it compares with other libraries.](#how-it-compares-with-other-libraries)
     - [The name: Ribbit](#the-name-ribbit)
@@ -26,10 +26,13 @@
 Using Ribbit, you might expect to see something like this:
 
 ```haskell
-type PeopleTable =
-  Field "id" Int
-  :> Field "name" Text
-  :> Field "age" Int
+data PeopleTable
+instance Table PeopleTable where
+  type Name PeopleTable = "people"
+  type DBSchema PeopleTable =
+    Field "id" Int
+    :> Field "name" Text
+    :> Field "age" Int
   
 
 type MyQuery = Select '["id", "name"] `From` PeopleTable `Where` "age" `Equals` (?)
@@ -148,13 +151,48 @@
   (Only 1)
 ```
 
+#### `UPDATE TABLE` support.
+
+Basic updates are supported:
+
+```haskell
+execute
+  conn
+  (Proxy :: Proxy (
+    Update PeopleTable
+      '[
+        "name", -- each field adds a query parameter so you can
+        "age"   -- provide the new value at run time.
+      ]
+    `Where` (
+      "id" `Equals` (?)
+    )
+  ))
+  (Only newName :> Only newAge :> Only targetEmployeeId)
+```
+
+
 ## Roadmap
 
 This is what I plan to work on next:
 
-### UPDATE support
+### Evaluate the usefulness of what I've got so far.
 
-Support update operations.
+I'm am half pleased with the way the query language turned out, because
+someone with understanding of SQL can at least read and modify queries,
+even if they would find it hard to construct them from scratch without
+examples, but I still feel they could be more ergonomic in at least two ways:
+
+1) Better error messages for compiler-guided development.
+2) Fewer backticks.
+
+I have tried playing around with defining the query language in terms
+of value level constructs, which I think can help improve both of these
+issues, but I haven't yet gotten that to work without requiring so much
+type annotation that it defeats the purpose of ergonomics. I have some
+vague idea about how I might solve that with generic instances, but I
+am reluctant to do anything that smacks of "magic" without exhausting
+all other possibilities first.
 
 ### Flesh out Haskell to PostgreSQL type mapping.
 
diff --git a/ribbit.cabal b/ribbit.cabal
--- a/ribbit.cabal
+++ b/ribbit.cabal
@@ -1,6 +1,6 @@
 
 name:                ribbit
-version:             0.4.0.0
+version:             0.4.1.0
 synopsis:            Type-level Relational DB combinators.
 description:         Ribbit is yet another type safe relational database
                      library for Haskell, heavily inspired by the
diff --git a/src/Database/Ribbit.hs b/src/Database/Ribbit.hs
--- a/src/Database/Ribbit.hs
+++ b/src/Database/Ribbit.hs
@@ -24,8 +24,8 @@
      interpreted by third parties for their own purposes.
 
   To that end, each schema is a new type, defined by you, using the
-  combinators provided by this library. The same goes for queries. Each
-  query is a separate type defined with combinators from this library.
+  constructors provided by this library. The same goes for queries. Each
+  query is a separate type defined with constructors from this library.
 
   We provide a PostgreSQL backend so that real work can be accomplished,
   but if the backend is missing something you need, then the idea is
@@ -50,24 +50,31 @@
   -- ** Deleting values
   -- $delete
 
+  -- ** Updating values
+  -- $update
+
   -- * Schema Definition Types
   (:>)(..),
   Table(..),
   Field,
 
-  -- * Query Combinators
+  -- * SQL Statement Constructors
+  -- ** Query Constructors
   Select,
   From,
   As,
-  Where,
 
-  -- * Insert Combinators
+  -- ** Insert Constructors
   InsertInto,
   
-  -- * Delete Combinators
+  -- ** Delete Constructors
   DeleteFrom,
 
+  -- ** Update Constructors
+  Update,
+
   -- ** Condition Conbinators
+  Where,
   And,
   Or,
   Equals,
@@ -78,16 +85,16 @@
   Lte,
   Not,
 
-  -- ** Query Parameters
+  -- ** Statement Parameters
   type (?),
 
-  -- * Transformations on Query Types
+  -- * Transformations on Statement Types
   ArgsType,
   ResultType,
   ValidField,
   ProjectionType,
 
-  -- * Query Rendering
+  -- * Statement Rendering
   Render(..)
 
 ) where
@@ -156,7 +163,7 @@
 -- >     :> Field "birth_date" Day
 
 -- $query
--- To write queries against these tables, use the query combinators
+-- To write queries against these tables, use the query constructors
 -- defined in this module:
 -- 
 -- > -- Given a company name as a query parameter, return all the
@@ -295,74 +302,102 @@
 -- >     (Proxy :: Proxy DeleteAllEmployees)
 -- >     ()
 
+-- $update
+-- Updating values is almost the same as inserting values. Instead of
+-- specifying the fields that get inserted, you specify the fields that get
+-- updated, along with the conditions that match the rows to be updated.
+--
+-- > {- Update an employee's salary (hopefully a raise!). -}
+-- > type UpdateSalary =
+-- >   Update
+-- >     '[
+-- >       "salary"
+-- >     ]
+-- >   `Where` 
+-- >     "id" `Equals` (?)
+-- > 
+-- > ...
+-- > 
+-- > let
+-- >   newSalary :: Int
+-- >   newSalary = ...
+-- > 
+-- >   targetEmployee :: Int
+-- >   targetEmployee = ...
+-- > in
+-- >   execute
+-- >     conn
+-- >     (Proxy :: Proxy UpdateSalary)
+-- >     (Only newSalary :> Only targetEmployee)
 
-{- | "SELECT" combinator, used for starting a @SELECT@ statement. -}
+
+{- | "SELECT" constructor, used for starting a @SELECT@ statement. -}
 data Select fields
 
 
 {- |
-  "FROM" combinator, used for attaching a SELECT projection to a relation
+  "FROM" constructor, used for attaching a SELECT projection to a relation
   in the database.
 -}
 data From proj relation
 infixl 6 `From`
 
 
-{- | "WHERE" combinator, used for attaching conditions to a query. -}
+{- | "WHERE" constructor, used for attaching conditions to a query. -}
 data Where query conditions
 infixl 6 `Where`
 
 
-{- | "=" combinator for conditions. -}
+{- | "=" constructor for conditions. -}
 data Equals l r
 infix 9 `Equals`
 
 
-{- | "!=" combinator for conditions. -}
+{- | "!=" constructor for conditions. -}
 data NotEquals l r
 infix 9 `NotEquals`
 
 
-{- | "<" combinator for conditions. -}
+{- | "<" constructor for conditions. -}
 data Lt l r
 infix 9 `Lt`
 
 
-{- | "<=" combinator for conditions. -}
+{- | "<=" constructor for conditions. -}
 data Lte l r
 infix 9 `Lte`
 
 
-{- | ">" combinator for conditions. -}
+{- | ">" constructor for conditions. -}
 data Gt l r
 infix 9 `Gt`
 
 
-{- | ">=" combinator for conditions. -}
+{- | ">=" constructor for conditions. -}
 data Gte l r
 infix 9 `Gte`
 
 
-{- | "AND" combinator for conditions. -}
+{- | "AND" constructor for conditions. -}
 data And l r
 infixr 8 `And`
 
 
-{- | "OR" combinator for conditions. -}
+{- | "OR" constructor for conditions. -}
 data Or l r
 infixr 7 `Or`
 
 
-{- | "AS" combinator, used for attaching a name to a table in a FROM clause. -}
+{- | "AS" constructor, used for attaching a name to a table in a FROM clause. -}
 data As relation name
 infix 8 `As`
 
 
-{- | NOT conditional combinator. -}
+{- | NOT conditional constructor. -}
 data Not a
 
 
-{- | "?" combinator, used to indicate the presence of a query parameter. -}
+{- | "?" constructor, used to indicate the presence of a query parameter. -}
 data (?)
 
 
@@ -527,7 +562,13 @@
     TypeError ('Lit.Text "Insert statement must specify at least one column.")
   ArgsType (InsertInto relation fields) =
     ProjectionType fields (DBSchema relation)
+  ArgsType (Update relation fields) =
+    ProjectionType fields (DBSchema relation)
+  ArgsType (Update relation fields `Where` conditions) =
+    ProjectionType fields (DBSchema relation)
+    :> ArgsType (DBSchema relation, conditions)
 
+
   ArgsType (schema, And a b) =
     StripUnit (Flatten (ArgsType (schema, a) :> ArgsType (schema, b)))
   ArgsType (schema, Or a b) =
@@ -722,13 +763,37 @@
         <> " (" <> T.intercalate ", " fields <> ")"
         <> " values (" <> T.intercalate ", " (const "?" <$> fields) <> ");"
 
-
 {- DELETE -}
 instance (KnownSymbol (Name table)) => Render (DeleteFrom table) where
   render _proxy =
     "delete from " <> symbolVal (Proxy @(Name table))
 
+{- UPDATE -}
+instance (KnownSymbol (Name table), RenderUpdates updates)
+  =>
+    Render (Update table updates)
+  where
+    render _proxy =
+      "UPDATE "
+      <> symbolVal (Proxy @(Name table))
+      <> " SET "
+      <> renderUpdates (Proxy @updates)
 
+
+{- | Render the updates to a table. -}
+class RenderUpdates a where
+  renderUpdates :: proxy a -> Text
+instance (KnownSymbol field) => RenderUpdates '[field] where
+  renderUpdates _ = symbolVal (Proxy @field) <> " = ?"
+instance (KnownSymbol field, RenderUpdates (m:ore))
+  =>
+    RenderUpdates (field : (m:ore))
+  where
+    renderUpdates _ =
+      symbolVal (Proxy @field) <> " = ?, "
+      <> renderUpdates (Proxy @(m:ore))
+
+
 {- | Insert statement. -}
 data InsertInto table fields
 
@@ -744,5 +809,9 @@
 
 {- | Delete statement. -}
 data DeleteFrom table
+
+
+{- | Update rows in a table. -}
+data Update table fields
 
 
