diff --git a/src/Database/YeshQL.hs b/src/Database/YeshQL.hs
--- a/src/Database/YeshQL.hs
+++ b/src/Database/YeshQL.hs
@@ -38,7 +38,7 @@
 ...will create a top-level function of type:
 
 @
-    insertUser :: IConnection conn => conn -> String -> IO [Integer]
+    insertUser :: IConnection conn => conn -> String -> IO (Maybe Integer)
 @
 
 == Syntax
@@ -62,7 +62,7 @@
     -- :id :: Integer
     SELECT id, name FROM users WHERE id = :id
     ;;;
-    -- name:getUserEx :: (Integer, String)
+    -- name:getUserEx :: [(Integer, String)]
     -- :id :: Integer
     -- :filename :: String
     SELECT id, name FROM users WHERE name = :filename OR id = :id
@@ -85,25 +85,35 @@
 @
 
 This line tells YeshQL to generate an object called @insertUser@, which should
-be a function of type @IConnection conn => conn -> {...} -> IO (Integer)@
+be a function of type @IConnection conn => conn -> {...} -> IO (Maybe Integer)@
 (where the @{...}@ part depends on query parameters, see below).
 
 The declared return type can be one of the following:
 
 - (); the generated function will ignore any and all results from the query
   and always return ().
-- An integer scalar, e.g. 'Integer' or 'Int'; the generated function will
-  return a row count from @INSERT@ / @UPDATE@ / ... statements, or 0 from
-  @SELECT@ statements.
+- The keyword 'rowcount', followed by an integer scalar, e.g.
+  'Integer' or 'Int'; the generated function will return a row count from
+  @INSERT@ / @UPDATE@ / ... statements, or 0 from @SELECT@ statements.
 - A tuple, where all elements implement 'FromSql'; the function will return
-  the result set from a @SELECT@ query as a list of tuples, or an empty list
-  for other query types.
-- A "one-tuple", i.e., a type in parentheses. The return value will be a list
+  the result set from a @SELECT@ query as a 'Maybe' of such tuples, or always
+  'Nothing' for other query types. For example, @:: (String, Int)@ produces
+  a function whose type ends in @conn -> IO (Maybe (String, Int))@.
+- A scalar, i.e., just a type name. The return value will be a 'Maybe'
   of scalars, containing the values from the first (or only) column in
   the result set. Note that, unlike Haskell, YeshQL does distinguish between
   @Type@ and @(Type)@: the former is a scalar type, while the latter is a
-  one-tuple whose only element is of type @Type@.
+  one-tuple whose only element is of type @Type@. For example, @:: (Int)@
+  produces a function whose type ends in @... -> conn -> IO (Maybe Int))@.
+- A list of tuples, e.g. @[(String, Int)]@; the return value will be a list
+  of such tuples.
+- A list of scalars, e.g. @[Int]@. The return value will be a list of
+  scalars, i.e., @... -> conn -> IO [Int]@.
 
+Scalars can be written as "one-tuples", that is, @[Int]@ and @[(Int)]@ are
+equivalent.
+
+
 @
     -- :paramName :: Type
 @
@@ -233,6 +243,10 @@
 
 import Database.YeshQL.Parser
 
+headMay :: [a] -> Maybe a
+headMay [] = Nothing
+headMay (x:_) = Just x
+
 nthIdent :: Int -> String
 nthIdent i
     | i < 26 = [chr (ord 'a' + i)]
@@ -429,10 +443,13 @@
                     tupleT 0
                 else
                     case pqReturnType query of
-                        Left tn -> mkType tn
-                        Right [] -> tupleT 0
-                        Right (x:[]) -> appT listT $ mkType x
-                        Right xs -> appT listT $ foldl' appT (tupleT $ length xs) (map mkType xs)
+                        ReturnRowCount tn -> mkType tn
+                        ReturnTuple One [] -> tupleT 0
+                        ReturnTuple One (x:[]) -> appT [t|Maybe|] $ mkType x
+                        ReturnTuple One xs -> appT [t|Maybe|] $ foldl' appT (tupleT $ length xs) (map mkType xs)
+                        ReturnTuple Many [] -> tupleT 0
+                        ReturnTuple Many (x:[]) -> appT listT $ mkType x
+                        ReturnTuple Many xs -> appT listT $ foldl' appT (tupleT $ length xs) (map mkType xs)
 
 mkType :: ParsedType -> Q Type
 mkType (MaybeType n) = [t|Maybe $(conT . mkName $ n)|]
@@ -495,10 +512,10 @@
 
         convert :: ExpQ
         convert = case pqReturnType query of
-                    Left tn -> varE 'fromInteger
-                    Right [] -> [|\_ -> ()|]
-                    Right (x:[]) -> [|map (fromSql . head)|]
-                    Right xs ->
+                    ReturnRowCount tn -> varE 'fromInteger
+                    ReturnTuple _ [] -> [|\_ -> ()|]
+                    ReturnTuple _ (x:[]) -> [|map (fromSql . head)|]
+                    ReturnTuple _ xs ->
                         let varNames = map nthIdent [0..pred (length xs)]
                         in [|map $(lamE
                                     -- \[a,b,c,...] ->
@@ -506,8 +523,12 @@
                                     -- (fromSql a, fromSql b, fromSql c, ...)
                                     (tupE $ (map (\n -> appE (varE 'fromSql) (varE . mkName $ n)) varNames)))|]
         queryFunc = case pqReturnType query of
-                        Left _ -> [| \qstr params conn -> $convert <$> run conn qstr params |]
-                        Right _ -> [| \qstr params conn -> $convert <$> quickQuery' conn qstr params |]
+                        ReturnRowCount _ ->
+                            [| \qstr params conn -> $convert <$> run conn qstr params |]
+                        ReturnTuple Many _ ->
+                            [| \qstr params conn -> $convert <$> quickQuery' conn qstr params |]
+                        ReturnTuple One _ ->
+                            [| \qstr params conn -> fmap headMay $ $convert <$> quickQuery' conn qstr params |]
         rawQueryFunc = [| \qstr conn -> runRaw conn qstr |]
     if pqDDL query
         then
diff --git a/src/Database/YeshQL/Parser.hs b/src/Database/YeshQL/Parser.hs
--- a/src/Database/YeshQL/Parser.hs
+++ b/src/Database/YeshQL/Parser.hs
@@ -5,6 +5,8 @@
 , parseQueriesN
 , ParsedQuery (..)
 , ParsedType (..)
+, ParsedReturnType (..)
+, OneOrMany (..)
 , pqTypeFor
 )
 where
@@ -21,6 +23,13 @@
 data ParsedType = PlainType String | MaybeType String | AutoType
     deriving Show
 
+data OneOrMany = One | Many
+    deriving (Show, Eq, Enum, Ord)
+
+data ParsedReturnType = ReturnRowCount ParsedType
+                      | ReturnTuple OneOrMany [ParsedType]
+                      deriving (Show)
+
 data ParsedQuery =
     ParsedQuery
         { pqQueryName :: String
@@ -28,7 +37,7 @@
         , pqParamsRaw :: [(String, ParsedType)]
         , pqParamNames :: [String]
         , pqParamTypes :: Map String ParsedType
-        , pqReturnType :: Either ParsedType [ParsedType]
+        , pqReturnType :: ParsedReturnType
         , pqDocComment :: String
         , pqDDL :: Bool
         }
@@ -41,7 +50,7 @@
             -> String
             -> [(String, ParsedType)]
             -> [(String, ParsedType)]
-            -> Either ParsedType [ParsedType]
+            -> ParsedReturnType
             -> String
             -> Bool
             -> ParsedQuery
@@ -57,7 +66,7 @@
         isDDL
 
 extractParamNames :: [(String, ParsedType)] -> [String]
-extractParamNames xs = 
+extractParamNames xs =
     nub . map fst $ xs
 
 extractParamTypeMap :: [(String, ParsedType)] -> Map String ParsedType
@@ -111,7 +120,7 @@
     not . null $ [ undefined | ParsedAnnotation DDLAnnotation <- items ]
 
 parseQueryN :: String -> String -> Either ParseError ParsedQuery
-parseQueryN fn src = 
+parseQueryN fn src =
     runParser mainP () fn src
 
 parseQuery :: String -> Either ParseError ParsedQuery
@@ -145,7 +154,7 @@
 queryP :: Parsec String () ParsedQuery
 queryP = do
     spaces
-    (qn, retType) <- option ("", Left (PlainType "Integer")) $ nameDeclP <|> namelessDeclP
+    (qn, retType) <- option ("", ReturnRowCount (PlainType "Integer")) $ nameDeclP <|> namelessDeclP
     extraItems <- many (try annotationP <|> paramDeclP <|> commentP)
     items <- many (try itemP <|> try commentP)
     return $ parsedQuery
@@ -157,18 +166,18 @@
                 (extractDocComment (extraItems ++ items))
                 (extractIsDDL (extraItems ++ items))
 
-nameDeclP :: Parsec String () (String, Either ParsedType [ParsedType])
+nameDeclP :: Parsec String () (String, ParsedReturnType)
 nameDeclP = do
     try (whitespaceP >> string "--" >> whitespaceP >> string "name" >> whitespaceP >> char ':')
     whitespaceP
     qn <- identifierP
     whitespaceP
-    retType <- option (Left (PlainType "Integer")) (try (string "::" >> whitespaceP >> returnTypeP))
+    retType <- option (ReturnRowCount (PlainType "Integer")) (try (string "::" >> whitespaceP >> returnTypeP))
     whitespaceP
     newlineP
     return (qn, retType)
 
-namelessDeclP :: Parsec String () (String, Either ParsedType [ParsedType])
+namelessDeclP :: Parsec String () (String, ParsedReturnType)
 namelessDeclP = do
     try (whitespaceP >> string "--" >> whitespaceP >> string "::" >> whitespaceP)
     retType <- returnTypeP
@@ -183,19 +192,39 @@
         leadCharP = oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ "_"
         tailCharP = oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"
 
-returnTypeP :: Parsec String () (Either ParsedType [ParsedType])
-returnTypeP = returnTypeMultiP <|> returnTypeSingleP
+returnTypeP :: Parsec String () ParsedReturnType
+returnTypeP = returnTypeRowcountP <|> returnTypeMultiP <|> returnTypeSingleP
 
-returnTypeSingleP :: Parsec String () (Either ParsedType [ParsedType])
-returnTypeSingleP = Left <$> typeP
+returnTypeRowcountP :: Parsec String () ParsedReturnType
+returnTypeRowcountP = do
+    try (string "rowcount")
+    whitespaceP
+    ReturnRowCount <$> typeP
 
-returnTypeMultiP :: Parsec String () (Either ParsedType [ParsedType])
+returnTypeMultiP :: Parsec String () ParsedReturnType
 returnTypeMultiP =
-    Right <$> between
+    ReturnTuple Many <$> between
+        (char '[' >> whitespaceP)
+        (char ']' >> whitespaceP)
+        returnTypeRowP
+
+returnTypeSingleP :: Parsec String () ParsedReturnType
+returnTypeSingleP =
+    ReturnTuple One <$> returnTypeRowP
+
+returnTypeRowP :: Parsec String () [ParsedType]
+returnTypeRowP =
+    returnTypeTupleP <|> 
+    fmap (:[]) typeP
+
+returnTypeTupleP :: Parsec String () [ParsedType]
+returnTypeTupleP =
+    between
         (char '(' >> whitespaceP)
         (char ')' >> whitespaceP)
         (sepBy (between whitespaceP whitespaceP typeP) (char ','))
 
+
 typeP :: Parsec String () ParsedType
 typeP = do
     name <- identifierP
@@ -266,6 +295,11 @@
 whitespaceP :: Parsec String () ()
 whitespaceP = do
     many (oneOf " \t\r")
+    return ()
+
+whitespace1P :: Parsec String () ()
+whitespace1P = do
+    many1 (oneOf " \t\r")
     return ()
 
 ignore :: Parsec s u a -> Parsec s u ()
diff --git a/yeshql.cabal b/yeshql.cabal
--- a/yeshql.cabal
+++ b/yeshql.cabal
@@ -1,7 +1,9 @@
 name: yeshql
-version: 0.3.0.3
+version: 1.0.0.0
 synopsis: YesQL-style SQL database abstraction
-description:
+description: Use quasi-quotations to write SQL in SQL, while at the same time
+             adding type annotations to turn them into well-typed Haskell
+             functions.
 license: MIT
 license-file: LICENSE
 author: Tobias Dammers
