diff --git a/Data/Iso/Common.hs b/Data/Iso/Common.hs
--- a/Data/Iso/Common.hs
+++ b/Data/Iso/Common.hs
@@ -7,6 +7,12 @@
 
   -- * @()@
   unit,
+  
+  -- * @(,)@
+  tup,
+  
+  -- * @(,,)@
+  tup3,
 
   -- * @Maybe a@
   nothing, just, maybe,
@@ -37,6 +43,17 @@
     f       t  = Just (() :- t)
     g (_ :- t) = Just t
 
+tup :: Iso (a :- b :- t) ((a, b) :- t)
+tup = Iso f g
+  where
+    f (a :- b :- t) = Just ((a, b) :- t)
+    g ((a, b) :- t) = Just (a :- b :- t)
+
+tup3 :: Iso (a :- b :- c :- t) ((a, b, c) :- t)
+tup3 = Iso f g
+  where
+    f (a :- b :- c :- t) = Just ((a, b, c) :- t)
+    g ((a, b, c) :- t) = Just (a :- b :- c :- t)
 
 nothing :: Iso t (Maybe a :- t)
 just    :: Iso (a :- t) (Maybe a :- t)
diff --git a/Data/Iso/Core.hs b/Data/Iso/Core.hs
--- a/Data/Iso/Core.hs
+++ b/Data/Iso/Core.hs
@@ -3,7 +3,7 @@
 module Data.Iso.Core (
 
   -- * Partial isomorphisms
-  Iso(..), convert, inverse,
+  Iso(..), convert, inverse, many,
   
   -- * Stack-based isomorphisms
   (:-)(..), stack, unstack, swap, duck,
@@ -17,7 +17,7 @@
 import Data.Monoid
 import Data.Semigroup
 
-import Control.Applicative
+import Control.Applicative hiding (many)
 import Control.Monad
 import Control.Category
 
@@ -48,6 +48,13 @@
 -- | Inverse of an isomorphism.
 inverse :: Iso a b -> Iso b a
 inverse (Iso f g) = Iso g f
+
+-- | Apply an isomorphism as many times as possible, greedily.
+many :: Iso a a -> Iso a a
+many (Iso f g) = Iso manyF manyG
+  where
+    manyF = ((<|>) <$> (f >=> manyF) <*> Just)
+    manyG = ((<|>) <$> (g >=> manyG) <*> Just)
 
 
 -- Stack-based isomorphisms
diff --git a/JsonGrammar.cabal b/JsonGrammar.cabal
--- a/JsonGrammar.cabal
+++ b/JsonGrammar.cabal
@@ -1,5 +1,5 @@
 Name:           JsonGrammar
-Version:        0.1
+Version:        0.2
 Synopsis:       Combinators for bidirectional JSON parsing
 Description:   	Combinators for bidirectional JSON parsing
 
@@ -9,7 +9,7 @@
 Stability:      Experimental
 Copyright:      Some Rights Reserved (CC) 2010 Martijn van Steenbergen
 Homepage:       https://github.com/MedeaMelana/JsonGrammar
-Bug-reports:    https://github.com/MedeaMelana/JsonGramamr/issues
+Bug-reports:    https://github.com/MedeaMelana/JsonGrammar/issues
 
 
 Cabal-Version:  >= 1.6
diff --git a/Language/JsonGrammar.hs b/Language/JsonGrammar.hs
--- a/Language/JsonGrammar.hs
+++ b/Language/JsonGrammar.hs
@@ -6,11 +6,11 @@
 
 module Language.JsonGrammar (
   -- * Constructing JSON grammars
-  liftAeson, option, greedyOption, array,
+  liftAeson, option, greedyOption, list, elementBy, array,
   propBy, rawFixedProp, rest, ignoreRest, object,
   
   -- * Type-directed conversion
-  Json(..), fromJson, toJson, litJson, prop, fixedProp
+  Json(..), fromJson, toJson, litJson, prop, fixedProp, element
   
   ) where
 
@@ -28,6 +28,8 @@
 import Data.String
 import Data.Text (Text)
 import qualified Data.Vector as V
+import qualified Data.Vector.Generic as VG
+import qualified Data.Vector.Fusion.Stream as VS
 
 
 aeObject :: Iso (Object :- t) (Value :- t)
@@ -54,19 +56,44 @@
 greedyOption :: Iso (Value :- t) (a :- t) -> Iso (Value :- t) (Maybe a :- t)
 greedyOption g = nothing . inverse aeNull <> just . g
 
--- | Describe an array whose elements match the given grammar.
-array :: Iso (Value :- t) (a :- t) -> Iso (Value :- t) ([a] :- t)
-array g = inverse aeArray >>> vectorList >>> elements
+-- | Convert between a JSON array and Haskell list of arbitrary lengts. The
+-- elements are converted using the argument grammar.
+list :: Iso (Value :- t) (a :- t) -> Iso (Value :- t) ([a] :- t)
+list g = duck nil >>> array (many single)
   where
-    elements = (inverse cons >>> swap >>> duck g >>> swap >>>
-                  duck elements >>> cons)
-            <> (inverse nil >>> nil)
+    -- With ScopedTypeVariables:
+    -- single :: Iso ([Value] :- [a] :- t) ([Value] :- [a] :- t)
+    single = swap                -- [a] :- [Value] :- t
+         >>> duck (elementBy g)  -- [a] :- [Value] :- a :- t
+         >>> swap                -- [Value] :- [a] :- a :- t
+         >>> duck swap           -- [Value] :- a :- [a] :- t
+         >>> duck cons           -- [Value] :- [a] :- t
 
-vectorList :: Iso (V.Vector a :- t) ([a] :- t)
-vectorList = stack (Iso f g)
+-- | Wrap a bunch of elements in a JSON array. For example, to match an array of exactly length two:
+--
+-- > array (element . element)
+--
+-- Or to match an empty array:
+--
+-- > array id
+array :: Iso ([Value] :- t1) ([Value] :- t2) -> Iso (Value :- t1) t2
+array els = inverse aeArray    -- Vector Value :- t1
+        >>> vectorReverseList  -- [Value] :- t1
+        >>> els                -- [Value] :- t2
+        >>> inverse nil        -- t2
+
+-- | Describe a single array element with the given grammar.
+elementBy :: Iso (Value :- t) (a :- t) ->
+  Iso ([Value] :- t) ([Value] :- a :- t)
+elementBy g = inverse cons  -- Value   :- [Value] :- t
+          >>> swap          -- [Value] :- Value :- t
+          >>> duck g        -- [Value] :- a :- t
+
+vectorReverseList :: Iso (V.Vector a :- t) ([a] :- t)
+vectorReverseList = stack (Iso f g)
   where
-    f = Just . V.toList
-    g = Just . V.fromList
+    f = Just . VS.toList    . VG.streamR
+    g = Just . VG.unstreamR . VS.fromList
 
 
 -- | Describe a property with the given name and value grammar.
@@ -119,7 +146,7 @@
   grammar :: Iso (Value :- t) (a :- t)
 
 instance Json a => Json [a] where
-  grammar = array grammar
+  grammar = list grammar
 
 instance Json Bool where
   grammar = liftAeson
@@ -174,3 +201,8 @@
 -- | Expect a specific key/value pair.
 fixedProp :: Json a => String -> a -> Iso (Object :- t) (Object :- t)
 fixedProp name value = rawFixedProp name (unsafeToJson "fixedProp" value)
+
+-- | Describe a single array element whose grammar is given by a 'Json'
+-- instance.
+element :: Json a => Iso ([Value] :- t) ([Value] :- a :- t)
+element = elementBy grammar
