tabulators
Stephen Weeks
MLton@sourcelight.com
Thu, 24 Jan 2002 17:33:19 -0800
> O.k. Then I would guess that an ITERATOR looks something like:
>
> signature ITERATOR =
...
> with:
>
> structure VectorIterator : ITERATOR =
...
Your code doesn't pass the type checker (maybe you cut and pasted
wrong)! Anyways, I don't see any need to include the 'b in the
iterator type. I was thinking of something more like
------------------------------------------------------------
signature ITERATE =
sig
type 'a t
datatype 'a iter = Iter of unit -> ('a * 'a iter) option
val iterate: 'a t -> 'a iter
val length: 'a t -> int (* for later *)
end
------------------------------------------------------------
This is still completely stateless, and lets you handle multiple
sequences simultaneously. For example,
------------------------------------------------------------
functor Fold2 (I: ITERATE):
sig
val fold2: 'a I.t * 'b I.t * 'c * ('a * 'b * 'c -> 'c) -> 'c
end =
struct
fun fold2 (va, vb, c, f) =
let
fun loop (ia, ib, c) =
let
val I.Iter ga = ia
val I.Iter gb = ib
in
case (ga (), gb ()) of
(NONE, NONE) => c
| (SOME (a, ia), SOME (b, ib)) => loop (ia, ib, f (a, b, c))
| _ => raise Fail "foreach2"
end
in
loop (I.iterate va, I.iterate vb, c)
end
end
------------------------------------------------------------
Actually, I think the right thing to do is to have a single ITER
structure matching the signature
signature ITER =
sig
datatype 'a t = T of unit -> ('a * 'a t) option
end
and then have all other signatures that expose an iterate function
export
val iterate: 'a t -> 'a Iter.t
Rounding out the rest of the signatures in the four boxes, we have.
stack no
------ ---------
construct unfold construct
destruct fold iterate
------------------------------------------------------------
signature CONSTRUCT =
sig
type 'a t
datatype 'a cons = Done of 'a t | More of 'a -> 'a cons
val construct: int -> 'a cons
end
signature FOLD =
sig
type 'a t
val fold: 'a t * 'b * ('a * 'b -> 'b) -> 'b
val length: 'a t -> int (* for later *)
end
signature UNFOLD =
sig
type 'a t
val unfold: int * 'b * ('b -> 'a * 'b) -> 'a t
end
------------------------------------------------------------
As you showed, it is easy to convert from iterate to fold, just as it
is easy to convert from construct to unfold.
------------------------------------------------------------
functor ItoF (S: ITERATE): FOLD =
struct
type 'a t = 'a S.t
fun fold (v, b, f) =
let
fun loop (S.Iter g, b) =
case g () of
NONE => b
| SOME (a, i) => loop (i, f (a, b))
in
loop (S.iterate v, b)
end
end
------------------------------------------------------------
By analogy with my earlier mail on constructors (tabulators at the
time), here are a couple of special cases of iterators. I have used
the same numbering scheme so you can see the analogy.
------------------------------------------------------------
signature ITERATE7 = ITERATE
signature ITERATE1 =
sig
type 'a t
val iterate: 'a t -> unit -> 'a
end
signature ITERATE2 =
sig
type 'a t
type 'a state
val iterate: 'a t -> {next: 'a state -> ('a * 'a state) option,
start: 'a state}
end
fun tooFew () = raise Fail "too few"
fun tooMany () = raise Fail "too many"
functor C7to1 (S: ITERATE7): ITERATE1 =
struct
type 'a t = 'a S.t
fun iterate v =
let
val r = ref (S.iterate v)
in
fn () => let
val S.Iter g = !r
in
case g () of
NONE => tooFew ()
| SOME (a, i) => (r := i; a)
end
end
end
functor C7to2 (S: ITERATE7): ITERATE2 =
struct
type 'a t = 'a S.t
type 'a state = 'a S.iter
fun iterate v =
{next = fn S.Iter g => g (),
start = S.iterate v}
end
------------------------------------------------------------
Here is how to pair up an iterator (which needs no stack) with an
unfolder (which needs the stack) to convert from one type to another.
------------------------------------------------------------
functor ConvIU (structure I: ITERATE
structure U: UNFOLD)
: sig
val conv: 'a I.t -> 'a U.t
end =
struct
fun conv v =
U.unfold (I.length v, I.iterate v,
fn I.Iter f =>
case f () of
NONE => tooFew ()
| SOME (a, i) => (a, i))
end
------------------------------------------------------------
There is a similar pairing of folders (which need the stack) with
constructors (which do not).
------------------------------------------------------------
functor ConvFC (structure C: CONSTRUCT
structure F: FOLD):
sig
val conv: 'a F.t -> 'a C.t
end =
struct
fun conv v =
case F.fold (v, C.construct (F.length v),
fn (a, c) =>
case c of
C.Done _ => tooMany ()
| C.More g => g a) of
C.Done v' => v'
| C.More _ => tooFew ()
end
------------------------------------------------------------
Finally, one can form a converter by pairing up both boxes that don't
need the stack, but then, of course, one must do the looping oneself.
------------------------------------------------------------
functor ConvIC (structure C: CONSTRUCT
structure I: ITERATE)
: sig
val conv: 'a I.t -> 'a C.t
end =
struct
fun conv v =
let
fun loop (c, i) =
case c of
C.Done v => v
| C.More f =>
let
val I.Iter g = i
in
case g () of
NONE => raise Fail "length mistmatch"
| SOME (a, i) => loop (f a, i)
end
in
loop (C.construct (I.length v), I.iterate v)
end
end
------------------------------------------------------------