diff --git a/arr.sml b/arr.sml
index bf5f6bcbb612c28c03169809e0736a4135c94864..19ddee1feadd2022ba66fe265690b7c2c0d2ec0e 100644
--- a/arr.sml
+++ b/arr.sml
@@ -11,6 +11,7 @@              | Nd of arr * arr (* (val, next) *)
              | Bx of arr * arr (* (shape, arr) *)
 with
 exception Domain;
+exception Index;
 exception BadStop;
 exception NYI;
 fun Word(x) = Lf(N(x));
@@ -26,15 +27,16 @@ fun tally(Lf(STOP)) = 0w0
   | tally(Lf(a:atom)) = 0w1
   | tally(Nd(x,y)) = 0w1 + tally y
   | tally(Bx(s,a)) = tally (unbox a);
-fun Box(a:arr) = Bx(Array([Word (tally a)]),a); (* TODO: Unbox? *)
-fun shape(Bx(s,a)) = Box(s);
+fun Box(a:arr) = Bx(Array([Word(tally a)]),a);
+fun shape(Bx(s,a)) = Box(s)
+  | shape(Nd(x,y)) = Box(Array([Word(tally(Nd(x,y)))])); (* TODO: Do we want a boxed array to be length 1 of the length of the contents? *)
 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)
-  | str(Bx(s,a)) = "[ " ^ str(a) ^ "]"
+  | str(Bx(s,a)) = "[ " ^ str(a) ^ "]";
 (* TODO: higher-order functions *)
 fun apply0(f) = raise NYI; (* TODO - what if it generates a whole array? *)
 fun apply1(f,Lf(STOP)) = Lf(STOP) (* traverse the array tree, applying f to each leaf in turn *)
@@ -42,6 +44,12 @@   | apply1(f,Lf(N(x))) = Lf(N(f(x)))
   | apply1(f,Nd(x,y)) = Nd(apply1(f,x),apply1(f,y));
 fun apply2(f,x,y) = raise NYI; (* TODO *)
 fun append(x,a) = Nd(x,a);
+fun index(Lf(STOP),_) = raise Index
+  | index(Lf(x),0w0) = Lf(x)
+  | index(Lf(x),_) = raise Index
+  | index(Nd(x,y),0w0) = x
+  | index(Nd(x,y),i) = index(y,i-0w1)
+  | index(Bx(s,a),i) = index((unbox a),i);
 (* fun addD(x,Lf(STOP)) = x (* dyadic add *)
   | addD(Lf(N(x)),Lf(N(y))) = Lf(N(x+y))
   | addD(Nd(x,xs),Lf(N(y))) = Nd(Lf(N(x+y)),add(xs,Lf(N(y)))) (* FIXME: doesn't
