diff --git a/futhark.cabal b/futhark.cabal
--- a/futhark.cabal
+++ b/futhark.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 3b4f60b44c2ac9e9de368d6116299bafb8b535f9a6ae7a26613bca65b22ad1d9
+-- hash: 27326a3d0b5daec736d8b0de8fda5938beaf2ee6e75215a5cfb6a29b1008afb0
 
 name:           futhark
-version:        0.7.3
+version:        0.7.4
 synopsis:       An optimising compiler for a functional, array-oriented language.
 description:    See the website at https://futhark-lang.org
 category:       Language
@@ -36,6 +36,7 @@
     rts/csharp/panic.cs
     rts/csharp/reader.cs
     rts/csharp/scalar.cs
+    rts/futhark-doc/style.css
     rts/python/__init__.py
     rts/python/memory.py
     rts/python/opencl.py
@@ -312,7 +313,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -333,7 +335,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -370,7 +371,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -391,7 +393,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -428,7 +429,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -449,7 +451,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -486,7 +487,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -507,7 +509,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -544,7 +545,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -565,7 +567,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -602,7 +603,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -623,7 +625,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -660,7 +661,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -681,7 +683,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -718,7 +719,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -739,7 +741,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -776,7 +777,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -797,7 +799,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -834,7 +835,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -855,7 +857,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -892,7 +893,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -913,7 +915,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
@@ -950,7 +951,8 @@
       Paths_futhark
   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg"
   build-depends:
-      ansi-terminal >=0.6.3.1
+      aeson
+    , ansi-terminal >=0.6.3.1
     , array >=0.4
     , base >=4 && <5
     , bifunctors >=5.4.2
@@ -971,7 +973,6 @@
     , http-client >=0.5.7.0
     , http-client-tls >=0.3.5.1
     , http-conduit >=2.2.4
-    , json
     , language-c-quote >=0.12
     , mainland-pretty >=0.6.1
     , markdown >=0.1.16
diff --git a/rts/futhark-doc/style.css b/rts/futhark-doc/style.css
new file mode 100644
--- /dev/null
+++ b/rts/futhark-doc/style.css
@@ -0,0 +1,226 @@
+body {
+    background-color: #fff9e5;
+    font-family: sans-serif;
+    padding: 0px;
+    margin: 0px;
+    margin-top: 0em;
+    margin-left: 0em;
+    margin-right: 0em;
+    overflow-y: scroll;
+    line-height: 1.4;
+}
+
+h1 {
+    margin-top: 0;
+    display: inline;
+}
+
+h2 {
+    margin-bottom: 1ex;
+    font-size: 2em;
+    color: #5f021f;
+}
+
+h3 {
+    font-size: 1em;
+    color: #5f021f;
+}
+
+p {
+    max-width: 65em;
+}
+
+td {
+    vertical-align: top;
+}
+
+#header {
+    color: #fff9e5;
+    background: #5f0220;
+    padding-left: 1em;
+}
+
+#navigation {
+    float: right;
+    vertical-align: centre;
+}
+
+#navigation ol {
+    display: block;
+}
+
+#navigation li {
+  display: inline;
+  list-style-type: none;
+  border-left: 1px solid #fff9e5;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+
+#navigation a, #navigation a:visited {
+    color: #fff9e5;
+    text-decoration: none;
+}
+
+#content {
+    display: flex;
+}
+
+main {
+    padding-left: 1em;
+    padding-right: 1em;
+    width: 100%;
+}
+
+#abstract {
+    margin-bottom: 2ex;
+}
+
+#footer {
+    color: #fff9e5;
+    background: #5f0220;
+    padding-left: 1em;
+    padding-right: 1em;
+}
+
+#footer a, #footer a:visited {
+    color: #fff9e5;
+}
+
+@media (max-width:950px) {
+    #filenav {
+        display: none;
+    }
+}
+
+#filenav {
+    color: #fff9e5;
+    background: #5f0220;
+    padding-left: 1em;
+    padding-right: 1em;
+}
+
+#filenav a:hover {
+    background: #fff9e5;
+    display: block;
+    color: #5f0220;
+}
+
+#filenav a, #footer a:visited {
+    color: #fff9e5;
+    text-decoration: none;
+}
+
+#filenav > ul {
+    list-style: none;
+    padding: 0;
+    font-family: monospace;
+    font-weight: bold;
+}
+
+.file_desc {
+    /* Hack to avoid breaking a file description across multiple columns
+       on the contents page. */
+    display: table;
+    width: 100%;
+}
+
+@media (min-width:950px) {
+    .file_list {
+        columns: 3;
+    }
+
+    .file_list > dd {
+        break-before: avoid;
+    }
+}
+
+.keyword {
+    font-weight: bold;
+}
+
+.specs {
+    margin-left: 2ex;
+    border-collapse: collapse;
+}
+
+.specs tr:hover td {
+    background: #ccc;
+}
+
+.spec_eql {
+    text-align: right;
+}
+
+.decl_description {
+    margin-bottom: 2ex;
+}
+
+.decl_name {
+    font-weight: bold;
+}
+
+.desc_header {
+    background: #ffcc7a;
+    font-family: monospace;
+    border-top: 1px solid #5f021f;
+}
+
+.desc_doc {
+    margin-left: 2ex;
+}
+
+.synopsis_link {
+    font-weight: bold;
+    text-decoration: none;
+    font-size: 1.3em;
+    width: 2ex;
+    display: inline-block;
+}
+
+.self_link {
+    text-decoration: none;
+}
+
+#module {
+    font-family: monospace;
+    white-space: pre;
+    display: block;
+    unicode-bidi: embed;
+}
+
+#doc_index thead {
+    font-weight: bold;
+}
+
+#doc_index td {
+    padding-right: 1em;
+}
+
+.doc_index_name {
+    font-family: monospace;
+}
+
+.doc_index_namespace {
+    font-style: italic;
+}
+
+.doc_index_initial {
+    font-size: 2em;
+    font-weight: bold;
+}
+
+.doc_index_initial a, .doc_index_initial a:visited {
+    color: #5f0220;
+}
+
+#doc_index_list li {
+  display: inline;
+  list-style-type: none;
+  padding-right: 1em;
+}
+
+#doc_index_list a, #doc_index_list a:visited {
+    color: #5f0220;
+    text-decoration: none;
+}
diff --git a/src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs b/src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs
--- a/src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs
+++ b/src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs
@@ -580,10 +580,10 @@
 transposeKernelAndGroupSize :: ImpOpenCL.Exp -> ImpOpenCL.Exp -> ImpOpenCL.Exp
                             -> ([ImpOpenCL.Exp], [ImpOpenCL.Exp])
 transposeKernelAndGroupSize num_arrays x_elems y_elems =
-  ([x_elems `roundUpTo` transposeBlockDim ,
-    y_elems `roundUpTo` transposeBlockDim,
+  ([(x_elems `roundUpTo` transposeBlockDim) *
+    (y_elems `roundUpTo` transposeBlockDim) *
     num_arrays],
-   [transposeBlockDim, transposeBlockDim, 1])
+   [transposeBlockDim * transposeBlockDim])
 
 roundUpTo :: ImpOpenCL.Exp -> ImpOpenCL.Exp -> ImpOpenCL.Exp
 roundUpTo x y = x + ((y - (x `impRem` y)) `impRem` y)
diff --git a/src/Futhark/CodeGen/OpenCL/Kernels.hs b/src/Futhark/CodeGen/OpenCL/Kernels.hs
--- a/src/Futhark/CodeGen/OpenCL/Kernels.hs
+++ b/src/Futhark/CodeGen/OpenCL/Kernels.hs
@@ -89,37 +89,44 @@
   case transpose_type of
     TransposeNormal ->
       bigKernel []
-      [C.cexp|get_global_id(0)|]
-      [C.cexp|get_global_id(1)|]
-      [C.cexp|get_group_id(1) * FUT_BLOCK_DIM + get_local_id(0)|]
-      [C.cexp|get_group_id(0) * FUT_BLOCK_DIM + get_local_id(1)|]
+      [C.cexp|global_id_x|]
+      [C.cexp|global_id_y|]
+      [C.cexp|group_id_y * FUT_BLOCK_DIM + local_id_x|]
+      [C.cexp|group_id_x * FUT_BLOCK_DIM + local_id_y|]
+      (toNumGroups [C.cexp|width|])
+      (toNumGroups [C.cexp|height|])
     TransposeLowWidth ->
       bigKernel [C.cparams|uint muly|]
-      [C.cexp|get_group_id(0) * FUT_BLOCK_DIM + (get_local_id(0) / muly)|]
-      [C.cexp|get_group_id(1) * FUT_BLOCK_DIM * muly
-           + get_local_id(1)
-           + (get_local_id(0) % muly) * FUT_BLOCK_DIM
+      [C.cexp|group_id_x * FUT_BLOCK_DIM + (local_id_x / muly)|]
+      [C.cexp|group_id_y * FUT_BLOCK_DIM * muly
+           + local_id_y
+           + (local_id_x % muly) * FUT_BLOCK_DIM
           |]
-      [C.cexp|get_group_id(1) * FUT_BLOCK_DIM * muly
-           + get_local_id(0)
-           + (get_local_id(1) % muly) * FUT_BLOCK_DIM|]
-      [C.cexp|get_group_id(0) * FUT_BLOCK_DIM + (get_local_id(1) / muly)|]
+      [C.cexp|group_id_y * FUT_BLOCK_DIM * muly
+           + local_id_x
+           + (local_id_y % muly) * FUT_BLOCK_DIM|]
+      [C.cexp|group_id_x * FUT_BLOCK_DIM + (local_id_y / muly)|]
+      (toNumGroups [C.cexp|width|])
+      (toNumGroups [C.cexp|(height + muly - 1) / muly|])
     TransposeLowHeight ->
       bigKernel [C.cparams|uint mulx|]
-      [C.cexp|get_group_id(0) * FUT_BLOCK_DIM * mulx
-           + get_local_id(0)
-           + (get_local_id(1) % mulx) * FUT_BLOCK_DIM
+      [C.cexp|group_id_x * FUT_BLOCK_DIM * mulx
+           + local_id_x
+           + (local_id_y % mulx) * FUT_BLOCK_DIM
           |]
-      [C.cexp|get_group_id(1) * FUT_BLOCK_DIM + (get_local_id(1) / mulx)|]
-      [C.cexp|get_group_id(1) * FUT_BLOCK_DIM + (get_local_id(0) / mulx)|]
-      [C.cexp|get_group_id(0) * FUT_BLOCK_DIM * mulx
-           + get_local_id(1)
-           + (get_local_id(0) % mulx) * FUT_BLOCK_DIM
+      [C.cexp|group_id_y * FUT_BLOCK_DIM + (local_id_y / mulx)|]
+      [C.cexp|group_id_y * FUT_BLOCK_DIM + (local_id_x / mulx)|]
+      [C.cexp|group_id_x * FUT_BLOCK_DIM * mulx
+           + local_id_y
+           + (local_id_x % mulx) * FUT_BLOCK_DIM
            |]
+      (toNumGroups [C.cexp|(width + mulx - 1) / mulx|])
+      (toNumGroups [C.cexp|height|])
     TransposeSmall ->
       smallKernel
   where
-    bigKernel extraparams x_in_index y_in_index x_out_index y_out_index =
+    toNumGroups e = [C.cexp|($exp:e + FUT_BLOCK_DIM - 1) / FUT_BLOCK_DIM|]
+    bigKernel extraparams x_in_index y_in_index x_out_index y_out_index ngrpx ngrpy =
       [C.cfun|
        // This kernel is optimized to ensure all global reads and writes are coalesced,
        // and to avoid bank conflicts in shared memory.  The shared memory array is sized
@@ -129,6 +136,21 @@
        // Note that input_size/output_size may not equal width*height if we are dealing with
        // a truncated array - this happens sometimes for coalescing optimisations.
        __kernel void $id:kernel_name($params:params) {
+         uint num_groups_y = $exp:ngrpy;
+         uint num_groups_x = $exp:ngrpx;
+         uint num_groups_z = ((uint)get_num_groups(0)) /  (num_groups_y * num_groups_x);
+
+         uint local_id_y = ((uint)get_local_id(0)) / FUT_BLOCK_DIM;
+         uint local_id_x = ((uint)get_local_id(0)) % FUT_BLOCK_DIM;
+         uint group_id_z = ((uint)get_group_id(0)) / (num_groups_y * num_groups_x);
+         uint group_id_yx = ((uint)get_group_id(0)) % (num_groups_y * num_groups_x);
+         uint group_id_y = group_id_yx / num_groups_x;
+         uint group_id_x = group_id_yx % num_groups_x;
+
+         uint global_id_z = group_id_z;
+         uint global_id_y = group_id_y * FUT_BLOCK_DIM + local_id_y;
+         uint global_id_x = group_id_x * FUT_BLOCK_DIM + local_id_x;
+
          uint x_index;
          uint y_index;
          uint our_array_offset;
@@ -138,7 +160,7 @@
          idata += idata_offset/sizeof($ty:elem_type);
 
          // Adjust the input and output arrays for the third dimension.
-         our_array_offset = get_global_id(2) * width * height;
+         our_array_offset = global_id_z * width * height;
          odata += our_array_offset;
          idata += our_array_offset;
 
@@ -150,7 +172,7 @@
 
          if(x_index < width && y_index < height && index_in < input_size)
          {
-             block[get_local_id(1)*(FUT_BLOCK_DIM+1)+get_local_id(0)] = idata[index_in];
+             block[local_id_y*(FUT_BLOCK_DIM+1)+local_id_x] = idata[index_in];
          }
 
          barrier(CLK_LOCAL_MEM_FENCE);
@@ -163,7 +185,7 @@
 
          if(x_index < height && y_index < width && index_out < output_size)
          {
-             odata[index_out] = block[get_local_id(0)*(FUT_BLOCK_DIM+1)+get_local_id(1)];
+             odata[index_out] = block[local_id_x*(FUT_BLOCK_DIM+1)+local_id_y];
          }
        }|]
            where params = [C.cparams|__global $ty:elem_type *odata,
@@ -205,4 +227,4 @@
            if (get_global_id(0) < input_size) {
                odata[index_out] = idata[index_in];
            }
-         }|]
+}|]
diff --git a/src/Futhark/Internalise/Defunctionalise.hs b/src/Futhark/Internalise/Defunctionalise.hs
--- a/src/Futhark/Internalise/Defunctionalise.hs
+++ b/src/Futhark/Internalise/Defunctionalise.hs
@@ -451,9 +451,12 @@
       return ([], e, sv)
 defuncLet _ [] body (Info rettype) = do
   (body', sv) <- defuncExp body
-  case sv of
-    Dynamic _ -> return ([], body', Dynamic $ fromStruct $ removeShapeAnnotations rettype)
-    _         -> return ([], body', sv)
+  return ([], body', imposeType sv rettype )
+  where imposeType Dynamic{} t =
+          Dynamic $ fromStruct $ removeShapeAnnotations t
+        imposeType (RecordSV fs1) (Record fs2) =
+          RecordSV $ M.toList $ M.intersectionWith imposeType (M.fromList fs1) fs2
+        imposeType sv _ = sv
 
 -- | Defunctionalize an application expression at a given depth of application.
 -- Calls to dynamic (first-order) functions are preserved at much as possible,
@@ -476,7 +479,7 @@
 
       -- Inline certain trivial lifted functions immediately.  This is
       -- purely an optimisation to avoid having the rest of the
-      -- compiler spend a lot if time processing them (they will end
+      -- compiler spend a lot of time processing them (they will end
       -- up being inlined later anyway).  We also try to simplify away
       -- some let-bindings, to make the generated code look slightly
       -- more comprehensible.
diff --git a/src/Language/Futhark/Parser/Parser.y b/src/Language/Futhark/Parser/Parser.y
--- a/src/Language/Futhark/Parser/Parser.y
+++ b/src/Language/Futhark/Parser/Parser.y
@@ -277,10 +277,10 @@
       : val id TypeParams ':' TypeExpDecl
         { let L loc (ID name) = $2
           in ValSpec name $3 $5 Nothing (srcspan $1 $>) }
-      | val BindingBinOp ':' TypeExpDecl
-        { ValSpec $2 [] $4 Nothing (srcspan $1 $>) }
-      | val BindingUnOp ':' TypeExpDecl
-        { ValSpec $2 [] $4 Nothing (srcspan $1 $>) }
+      | val BindingBinOp TypeParams ':' TypeExpDecl
+        { ValSpec $2 $3 $5 Nothing (srcspan $1 $>) }
+      | val BindingUnOp TypeParams ':' TypeExpDecl
+        { ValSpec $2 $3 $5 Nothing (srcspan $1 $>) }
       | TypeAbbr
         { TypeAbbrSpec $1 }
       | type id TypeParams
diff --git a/src/futhark-bench.hs b/src/futhark-bench.hs
--- a/src/futhark-bench.hs
+++ b/src/futhark-bench.hs
@@ -14,6 +14,7 @@
 import Data.Maybe
 import Data.Semigroup ((<>))
 import Data.List
+import qualified Data.Map as M
 import qualified Data.Text as T
 import qualified Data.Text.IO as T
 import qualified Data.Text.Encoding as T
@@ -25,7 +26,7 @@
 import System.Timeout
 import System.Process.ByteString (readProcessWithExitCode)
 import System.Exit
-import qualified Text.JSON as JSON
+import qualified Data.Aeson as JSON
 import Text.Printf
 import Text.Regex.TDFA
 
@@ -56,24 +57,23 @@
 data DataResult = DataResult String (Either T.Text ([RunResult], T.Text))
 data BenchResult = BenchResult FilePath [DataResult]
 
-resultsToJSON :: [BenchResult] -> JSON.JSValue
-resultsToJSON = JSON.JSObject . JSON.toJSObject . map benchResultToJSObject
+resultsToJSON :: [BenchResult] -> JSON.Value
+resultsToJSON = JSON.toJSON . M.fromList . map benchResultToJSObject
   where benchResultToJSObject
           :: BenchResult
-          -> (String, JSON.JSValue)
+          -> (String, JSON.Value)
         benchResultToJSObject (BenchResult prog rs) =
-          (prog, JSON.JSObject $ JSON.toJSObject
-                 [("datasets", JSON.JSObject $ JSON.toJSObject $
-                               map dataResultToJSObject rs)])
+          (prog, JSON.toJSON $ M.fromList
+                 [("datasets" :: String, M.fromList $ map dataResultToJSObject rs)])
         dataResultToJSObject
           :: DataResult
-          -> (String, JSON.JSValue)
+          -> (String, JSON.Value)
         dataResultToJSObject (DataResult desc (Left err)) =
-          (desc, JSON.showJSON err)
+          (desc, JSON.toJSON $ show err)
         dataResultToJSObject (DataResult desc (Right (runtimes, progerr))) =
-          (desc, JSON.JSObject $ JSON.toJSObject
-                 [("runtimes", JSON.showJSON $ map runMicroseconds runtimes),
-                  ("stderr", JSON.showJSON progerr)])
+          (desc, JSON.toJSON $ M.fromList
+                 [("runtimes" :: String, JSON.toJSON $ map runMicroseconds runtimes),
+                  ("stderr", JSON.toJSON $ show progerr)])
 
 fork :: (a -> IO b) -> a -> IO (MVar b)
 fork f x = do cell <- newEmptyMVar
@@ -107,7 +107,7 @@
   results <- concat <$> mapM (runBenchmark opts) compiled_benchmarks
   case optJSON opts of
     Nothing -> return ()
-    Just file -> writeFile file $ JSON.encode $ resultsToJSON results
+    Just file -> LBS.writeFile file $ JSON.encode $ resultsToJSON results
   when (anyFailed results) exitFailure
 
   where ignored f = any (`match` f) $ optIgnoreFiles opts
