diff --git a/arr.sml b/arr.sml
new file mode 100644
index 0000000000000000000000000000000000000000..f9ee69a0b25b0948132b2e8fd6fc8a9b7ba3313e
--- /dev/null
+++ b/arr.sml
@@ -0,0 +1,29 @@
+(* Binary-tree implementation of arbitrary-depth arrays *)
+app load ["Word8"];
+
+abstype atom = N of Word8.word
+             | FN of unit -> arr
+             | FM of arr -> arr
+             | FD of (arr * arr) -> arr
+             | STOP
+and     arr  = Lf of atom (* arrays as binary trees - SML doesn't allow arbitrarily nested lists *)
+             | Nd of arr * arr
+with
+fun Word(x) = Lf(N(x));
+fun Fun0(f) = Lf(FN(f));
+fun Fun1(f) = Lf(FM(f));
+fun Fun2(f) = Lf(FD(f));
+fun Stop() = Lf(STOP);
+fun array [] = Stop()
+  | array (x::xs) = Nd(x,array(xs));
+fun str(Lf(STOP)) = "!!" (* TODO: box-drawing characters might be nice in the future *)
+  | str(Lf(N(x))) = Word8.fmt StringCvt.HEX x
+  | str(Lf(FN(x))) = "(Nilad)" (* TODO: would be nice to get actual function names *)
+  | str(Lf(FM(x))) = "(Monad)"
+  | str(Lf(FD(x))) = "(Dyad)"
+  | str(Nd(x,y)) = "[" ^ str(x) ^ " " ^ str(y) ^ "]"
+fun apply(f,Lf(STOP)) = Lf(STOP) (* traverse the array tree, applying f to each leaf in turn *)
+  | apply(f,Lf(N(x))) = Lf(N(f(x)))
+  (* TODO: higher-order functions *)
+  | apply(f,Nd(x,y)) = Nd(apply(f,x),apply(f,y));
+end;
diff --git a/ast.sml b/ast.sml
index 0cf45dd61c48584b1c0b09e874508b976bebc69f..d1c5335d0180cf42d734895fbaf6a7cf54b11fe1 100644
--- a/ast.sml
+++ b/ast.sml
@@ -1,18 +1,5 @@
 (* AST for a K-like language *)
 
-app load ["Word8"];
-
-datatype func = F0 of unit -> arr
-              | F1 of arr -> arr
-              | F2 of (arr * arr) -> arr
-and      atom = Num of Word8.word
-              (* | Fun of func *) (* FIXME: we want functions as atoms! *)
-and      arr  = Scalar of atom
-              | Array of arr list;
-
-datatype ''a arr = Scalar of ''a
-                 | Array of ''a list;
-
 datatype ast = Leaf of arr
              | Nilad of unit -> arr
              | Monad of (arr -> arr) * ast
@@ -20,28 +7,24 @@              | Dyad of ((arr * arr) -> arr) * ast * ast;
              (* TODO: higher-arity functions, projections &c.  Unless you just
                 want to do an APL rather than a K? *)
 
-(* Test data *)
-fun dup(x) = Array([x]@[x]);
-fun cat(x,y) = Array([x]@[y]);
-val x0 = Leaf(Scalar(Num(0w2)));
-val x1 = Leaf(Scalar(Num(0w3)));
-val y = Leaf(Array([Scalar(Num(0w3)),Scalar(Num(0w4))]));
-val m = Monad(dup,x0);
-val d = Dyad(cat,x0,x1);
-
-fun eval_atom(a) = case a of
-                        Num(x) => x;
-                      (* | Fun(x) => 0wxff; (* FIXME *) *)
-
-fun listify(a) = case a of
-                       Scalar(x) => listify(Array([Scalar(x)])) (* FIXME *)
-                     | Array(x)  => x;
-
-fun eval_scalar(s) = case s of
-                          Scalar(Num(s)) => s;
-
 fun eval_ast(t) = case t of
                        Leaf(a)     => a
                      | Nilad(f)    => f()
                      | Monad(f,r)  => f(eval_ast(r))
                      | Dyad(f,l,r) => f(eval_ast(l),eval_ast(r));
+(*
+(* Test data *)
+val x = S(N(0w2));
+val y = S(N(0w3));
+val a0 = A(x,y);
+val a1 = A(A(A(x,y),x),y);
+fun f() = S(N(0w4)); (* nilad *)
+fun g(x) = A(x,x); (* monad *)
+fun h(x,y) = A(x,y); (* dyad *)
+val t0 = Leaf(x);
+val t1 = Leaf(a0);
+val t2 = Leaf(a1);
+val tn = Nilad(f);
+val tm = Monad(g,t1);
+val td = Dyad(h,t1,t2);
+*)
