(* CTM Chapter #02 Examples in Alice ML *)
(* syntactic sugar for solutions using promises/futures *)
open Promise
open Future
infix 3 ?=
val op?= = fulfill
val ? = future
(* 2.1.1 Language syntax *)
fun fact 0 = 1
| fact n = n * fact (n-1);
(* 2.3.5 Basic operations *)
(* solution using state variables *)
let
val x = 5
val y = 10
val t = (x >= y)
val z = ref 0
in
if (t)
then z := x
else z := y
end;
let
val x = 5
val y = 10
val z = ref 0
in
if (x >= y)
then z := x
else z := y
end;
(* end solution using state variables *)
(* solution using promises/futures *)
let
val x = 5
val y = 10
val t = (x >= y)
val z = promise()
in
if (t)
then z ?= x
else z ?= y
end;
let
val x = 5
val y = 10
val z = promise()
in
if (x >= y)
then z ?= x
else z ?= y
end;
(* end solution using promises/futures *)
(* 2.4.1 Basic concepts *)
let
val a = 11
val b = 2
val c = a + b
val d = c * c
in
inspect d
end;
let
val x = 1
in
let
val x = 2
in
inspect x
end;
inspect x
end;
(* solution using state variables *)
val c = ref 0;
fun max x y z =
if (x >= y)
then z := x
else z := y;
max 3 5 c;
val c = ref 0;
val y = 5;
fun lb x z =
if (x >= y)
then z := x
else z := y;
lb 3 c;
let
val y = 10;
fun lb x z =
if (x >= y)
then z := x
else z := y
in
let
val y = 15;
val z = ref 0
in
lb 5 z
end
end;
(* end solution using state variables *)
(* solution using promises/futures *)
val c = promise();
fun max x y z =
if (x >= y)
then z ?= x
else z ?= y;
max 3 5 c;
val c = promise();
val y = 5;
fun lb x z =
if (x >= y)
then z ?= x
else z ?= y;
lb 3 c;
let
val y = 10;
fun lb x z =
if (x >= y)
then z ?= x
else z ?= y
in
let
val y = 15
val z = promise()
in
lb 5 z
end
end;
(* end solution using promises/futures *)
let
fun q x = (inspect ("stat(" ^ x ^ ")"))
fun p x = q x
in
let
fun q x = (inspect ("dyn(" ^ x ^ ")"))
in
p "hello"
end
end;
(* solution using state variables *)
let
val x = 10
val y = spawn 99
val z = ref 0
in
if (x >= y)
then z := x
else z := y
end;
(* end solution using state variables *)
(* solution using promises/futures *)
let
val x = 10
val y = spawn 99
val z = promise()
in
if (x >= y)
then z ?= x
else z ?= y
end;
(* end solution using promises/futures *)
(* 2.4.3 Nonsuspendable statements *)
(* solution using state variables *)
local
val res = ref (IntInf.fromInt(0))
in
local
val arg1 = IntInf.fromInt(111 * 111)
val arg2 = IntInf.fromInt(222 * 222)
val _ = res := arg1 * arg2
in
end;
val _ = inspect (!res);
end;
let
val y = 10
val z = ref 0
in
let
fun lowerBound x z =
if (x >= y)
then z := x
else z := y
in
lowerBound 5 z
end
end;
(* end solution using state variables *)
(* solution using promises/futures *)
local
val res = promise()
in
local
val arg1 = IntInf.fromInt(111 * 111)
val arg2 = IntInf.fromInt(222 * 222)
val _ = res ?= arg1 * arg2
in
end;
val _ = inspect res;
end;
let
val y = 10;
val z = promise()
in
let
fun lowerBound x z =
if (x >= y)
then z ?= x
else z ?= y
in
lowerBound 5 z
end
end;
(* end solution using promises/futures *)
(* 2.4.5 Basic concepts revisited *)
(* solution using state variables *)
let
fun max x y z =
if (x >= y)
then z := x
else z := y
in
let
val c = ref 0
in
max 3 5 c
end
end;
let
val y = 5;
fun lowerBound x z =
if (x >= y)
then z := x
else z := y
in
let
val c = ref 0
in
lowerBound 33 c
end
end;
let
val y = 5;
fun lowerBound x z =
if (x >= y)
then z := x
else z := y
in
let
val y = 10
val c = ref 0
in
lowerBound 33 c
end
end;
(* end solution using state variables *)
(* solution using promises/futures *)
let
fun max x y z =
if (x >= y)
then z ?= x
else z ?= y
in
let
val c = promise()
in
max 3 5 c
end
end;
let
val y = 5
fun lowerBound x z =
if (x >= y)
then z ?= x
else z ?= y
in
let
val c = promise()
in
lowerBound 33 c
end
end;
let
val y = 5
fun lowerBound x z =
if (x >= y)
then z ?= x
else z ?= y
in
let
val y = 10
val c = promise()
in
lowerBound 33 c
end
end;
(* end solution using state variables *)
(* 2.5.1 Last call optimization *)
fun loop10 i =
if (i = 10)
then
()
else
(
inspect i;
loop10 (i+1)
);
val i = loop10 0;
(* 2.5.4 Garbage collection is not magic *)
fun upto m n =
if (m > n)
then []
else m :: upto (m+IntInf.fromInt(1)) n;
val lst = upto (IntInf.fromInt(1)) (IntInf.fromInt(1000000));
fun sum x lst1 lst : IntInf.t =
case lst1 of
n::ns => sum (x+n) ns lst
| _ => x;
(* warning: this generates tons of garbage *)
(*inspect (sum (IntInf.fromInt(0)) lst lst));*)
fun sum x lst : IntInf.t =
case lst of
n::ns => sum (x+n) ns
| _ => x;
inspect (sum (IntInf.fromInt(0)) lst);
(* 2.6.1 Syntactic conveniences *)
datatype person = Person of { name:string, age:int };
Person{ name="George", age=25 };
let
val a = "George"
val b = 25
val x = Person{ name=a, age=b }
in
x
end;
datatype tree = Tree of { key:int, left:int, right:int, value:int };
val t = { key=1, left=2, right=3, value=4 };
let
val { key=a, left=b, right=c, value=d } = t
in
t
end;
11*11;
val x = 11*11;
Math.sqrt(5.0);
let
val xs = [5,6]
val ys = [3,4]
in
case (xs, ys) of
(nil, ys) => 1
| (xs, nil) => 2
| (x::xr, y::yr) if (x <= y) => 3
| _ => 4
end;
let
val xs = [5,6]
val ys = [3,4]
in
case xs of
nil => 1
| _ =>
case ys of
nil => 2
| _ =>
case xs of
x::xr => (
case ys of
y::yr =>
if (x <= y)
then 3
else 4
| _ => 4)
| _ => 4
end;
(2 = 2) andalso (4 = 4);
if (2 = 2) then (4 = 4) else false;
(2 = 2) orelse (4 = 4);
if (2 = 2) then true else (4 = 4);
(* don't think that alice has a correlary to nesting markers ($)
Oz: {Browse {Obj get($)}} *)
(* solution using state variables *)
let
fun obj z = z
fun get z = z := 1234
val x = ref 0
in
obj (get x);
inspect (!x)
end;
(* end solution using state variables *)
(* solution using promises/futures *)
let
fun obj z = z
fun get z = z ?= 1234
val x = promise()
in
obj (get x);
inspect (?x)
end;
(* end solution using promises/futures *)
(* 2.6.2 Functions *)
fun max x y =
if (x >= y)
then x
else y;
(* solution using state variables *)
val r = ref 0;
fun max x y =
r := (
if (x >= y)
then x
else y);
val r = ref 0;
fun max x y =
if (x >= y)
then r := x
else r := y;
(* end solution using state variables *)
(* solution using promises/futures *)
val r = promise();
fun max x y =
r ?= (
if (x >= y)
then x
else y);
val r = promise();
fun max x y =
if (x >= y)
then r ?= x
else r ?= y;
(* end solution using promises/futures *)
fun f x = x*x;
val x = 2;
val xr = [1, 2, 3, 4];
val ys = f x :: map f xr;
let
fun f x = x*x
val x = 2
val xr = [1, 2, 3, 4]
in
let
val y = f x
val yr = map f xr
in
y::yr
end
end;
fun map' f xs =
case xs of
nil => nil
| x::xr => (f x)::(map f xr);
inspect (map (fn (x) => (x * x)) [1, 2, 3, 4]);
let
fun f x = x*x
val x = 2
val xr = [1, 2, 3, 4]
val p = promise()
fun map' f xs p =
case xs of
nil => fulfill(p, nil)
| x::xs =>
let
val p' = promise()
val y = f x
val yr = future p'
in
fulfill(p, y::yr);
map' f xs p'
end
in
map' f xr p;
future p
end;
(* 2.6.3 Interactive interface *)
val px = promise()
and py = promise();
val x = future(px)
and y = future(py);
val px = promise()
and py = promise();
val x = future(px)
and y = future(py);
fulfill(px, 25);
datatype person = Person of { name:string, age:int };
val pa = promise();
val a = promise();
fulfill(pa, Person{ name="", age=25 });
val px = promise()
and py = promise();
val x = future(px)
and y = future(py);
let
val px = promise()
and py = promise()
val x = future(px)
and y = future(py)
in
()
end;
inspect 1;
let
val py = promise()
val y = future(py)
in
inspect y
end;
let
val pa = promise()
and pb = promise()
and pc = promise()
val a = future(pa)
and b = future(pb)
and c = future(pc)
in
spawn fulfill(pa, 10);
spawn fulfill(pb, 200);
fulfill(pc, a + b);
inspect c
end;
(* 2.7.1 Exceptions - Motivation and basic concepts *)
datatype 'a expression = Number of 'a
| Plus of 'a expression * 'a expression
| Minus of 'a expression * 'a expression
| Times of 'a expression * 'a expression;
exception IllFormedExpr of string;
fun toString(Number(x)) = "Number(" ^ Int.toString(x) ^ ")"
| toString(Plus(x, y)) = "Plus(" ^ toString(x) ^ ", " ^ toString(y) ^ ")"
| toString(Minus(x, y)) = "Minus(" ^ toString(x) ^ ", " ^ toString(y) ^ ")"
| toString(Times(x, y)) = "Times(" ^ toString(x) ^ ", " ^ toString(y) ^ ")";
fun isNumber(Number(x)) = true
| isNumber(_) = false;
fun getNumber(Number(x)) = x
| getNumber(e) = raise IllFormedExpr(toString(e));
fun eval e =
if (isNumber(e))
then getNumber(e)
else
case e of
Plus (x, y) => (eval x) + (eval y)
| Times (x, y) => (eval x) * (eval y)
| _ => raise IllFormedExpr(toString(e));
(
inspect (eval(Plus(Plus(Number(5), Number(5)), Number(10))));
inspect (eval(Times(Number(6), Number(11))));
inspect (eval(Minus(Number(7), Number(10))))
) handle IllFormedExpr(e) => inspect ("*** Illegal Expression: '" ^ e ^ "' ***");
(* 2.7.3 Exceptions - Full syntax *)
exception FileNotFound;
fun processFile f = raise FileNotFound;
fun closeFile f = ();
fun toString(FileNotFound) = "FileNotFound"
| toString(_) = "Unknown";
processFile "myfile"
handle failure => ()
finally closeFile "myfile";
processFile "myfile"
handle failure => inspect ("*** Exception '" ^ toString(failure) ^ "' when Processing File")
finally closeFile "myfile";
(* 2.7.4 Exceptions - Full syntax *)
fun one() = 1;
fun two() = 2;
(* compiler catches bind type errors *)
(* ({one} = {two}) handle failure => inspect ("caughtFailure"); *)
(* 2.8.1 Functional programming languages *)
fun $ x = x * x;
fun isNil x = (x = nil);
fun isCons (h::t) = true
| isCons nil = false;
fun car (h::t) = h
| car nil = nil;
fun cdr (h::t) = t
| cdr nil = nil;
fun cons (h, t) = h::t;
val rec append = fn (xs, ys) =>
if (isNil xs)
then ys
else cons(car xs, append((cdr xs), ys));
(* 2.8.2.1 Unification (the = operator) *)
(* Unify functions for Alice *)
exception Unify
fun isFulfilled p = not(Future.isFuture(future p))
fun known x =
let
val p = promise()
in
fulfill(p, x); p
end
fun unifyPromise unifyContent (p1, p2) =
if (p1 = p2)
then ()
else
case (isFulfilled p1, isFulfilled p2)
of (false, _) => fulfill(p1, future p2)
| (_, false) => fulfill(p2, future p1)
| (true, true) => unifyContent(future p1, future p2)
fun unifySimple(x, y) = if x = y then () else raise Unify
fun unifyList unifyElem (nil, nil) = ()
| unifyList unifyElem (x::xs, y::ys) =
(unifyElem(x,y); unifyList unifyElem (xs,ys))
| unifyList unifyElem _ = raise Unify;
unifyList unifySimple ([1,2,3], [1,2,3]); (* example usage *)
(* Person datatype used in the examples *)
datatype person = Person of {name : string promise, age : int promise}
fun unifyPerson(Person{name, age}, Person{name=name', age=age'}) = (
unifyPromise unifySimple (name, name');
unifyPromise unifySimple (age, age'))
(* Bindings to values *)
val x = Person{name=promise(), age=promise()}
val y = x
(* Unification is symetric *)
val x1 = promise()
val x2 = promise()
val x = Person{name=x1, age=x2};
unifyPerson(x, Person{name=x1, age=x2});
(* Any two partial values can be unified *)
unifyPerson(Person{name=promise(), age=promise()},
Person{name=known "George", age=known 25});
(* If partial values are already equal, then unification does nothing *)
unifyPerson(Person{name=known "George", age=known 25},
Person{name=known "George", age=known 25});
(* If partial values are incompatible, then they cannot be unified *)
unifyPerson(Person{name=promise(), age=known 26},
Person{name=known "George", age=known 25})
handle Unify => ();
(* Unification is symmetric in the arguments *)
unifyPerson(Person{name=known "George", age=promise()},
Person{name=promise(), age=known 25});
(* Unification can create cyclic structures *)
datatype person' = Person' of {grandfather : person' promise}
fun unifyPerson'(Person'{grandfather}, Person'{grandfather=grandfather'}) =
unifyPromise unifyPerson' (grandfather, grandfather')
val x = promise();
unifyPromise unifyPerson' (x, known(Person'{grandfather=x}));
(* Unification can bind cyclic structures *)
datatype t = F of {a : t promise, b : t promise}
fun unifyF (F{a, b}, F{a=a', b=b'}) =
(unifyPromise unifyF (a,a');
unifyPromise unifyF (b,b'))
val x = promise();
unifyPromise unifyF (x, known(F{a=x, b=promise()}));
val y = promise();
unifyPromise unifyF (y, known(F{a=promise(), b=y}));
unifyPromise unifyF (x, y);
(* 2.8.2.2 The unification algorithm *)
datatype foo = Foo of {a : int promise}
fun unifyFoo(Foo{a}, Foo{a=a'}) = (unifyPromise unifySimple (a, a'))
val x8 = promise()
val x7 = x8
val x6 = promise()
val x5 = promise()
val x4 = x5
val x3 = x4
val x2 = known 25;
val x1 = known (Foo{a=x2});
val x1 = promise()
val x2 = promise()
val x3 = promise()
val x4 = promise()
val x5 = promise()
val x6 = promise()
val x7 = promise()
val x8 = promise();
unifyPromise unifySimple (x1, known(Foo{a=x2}));
unifyPromise unifySimple (x2, known(25));
unifyPromise unifyFoo (x3, x6);
unifyPromise unifyFoo (x4, x6);
unifyPromise unifyFoo (x5, x6);
unifyPromise unifyFoo (x7, known(Foo{a=x2}));
unifyPromise unifyFoo (x8, known(Foo{a=x2}));
datatype foo = Foo of {a : foo promise}
fun unifyFoo(Foo{a}, Foo{a=a'}) = (unifyPromise unifySimple (a, a'))
datatype t = F of {a : t promise}
fun unifyF (F{a}, F{a=a'}) = unifyPromise unifyF (a,a')
val x = promise();
val y = promise();
unifyPromise unifyF (x, known(F{a=x}));
unifyPromise unifyF (y, known(F{a=y}));
(* loops forever on unify *)
(* unifyPromise unifyF (x, y); *)
(* 2.8.2.3 Displaying cyclic structures *)
(* ctm plays fast and loose with types in this section *)
(* not gonna spend much time getting these correct *)
datatype t = F of {a : t promise, b : t promise}
fun unifyF (F{a, b}, F{a=a', b=b'}) =
(unifyPromise unifyF (a,a');
unifyPromise unifyF (b,b'))
val x = promise();
val y = promise();
val z = promise();
unifyPromise unifyF (known(F{a=x, b=promise()}), known(F{a=promise(), b=y}));
unifyPromise unifyF (z, known(F{a=z, b=promise()}));
inspect [x, y, z];
datatype t = A of {a : t promise, b : t promise, c : t promise}
fun unifyA (A{a, b, c}, A{a=a', b=b', c=c'}) =
(unifyPromise unifyA (a,a');
unifyPromise unifyA (b,b');
unifyPromise unifyA (c,c'))
val x = promise();
val y = promise();
val z = promise();
(*unifyPromise unifyA (known(A{a=x, b=z, c=z}), known(A{a=y, b=y, c=x}));*)
inspect [x, y, z];
(* 2.8.2.4 Entailment and disentailment checks (the == and \= operations *)
val head = 1;
val tail = 2::nil;
val l1 = known(head::tail);
val l2 = known([1,2]);
val l3 = known(1::2::nil);
inspect (future(l1) = future(l2));
inspect (future(l1) = future(l3));
val x = promise();
val l1 = known([1]);
val l2 = known([future(x)]);
fulfill(x, 1); (* will wait if this line removed *)
inspect (future(l1) = future(l2));
val x = promise();
val l1 = known([future(x)]);
val l2 = known([future(x)]);
fulfill(x, 1); (* will wait if this line removed *)
inspect (future(l1) = future(l2));
val x = promise();
val l1 = known([1, future(promise())]);
val l2 = known([future(x), future(promise())]);
(*inspect (future(l1) = future(l2));*)
(* Alternate implementation of Unify *)
(* first stab at Unify in Alice - Andreas provided the cleaner solutions listed above *)
(* Unify functions for Alice *)
signature UNIFY =
sig
exception Unify
val clear : unit -> unit
val unify : unit -> unit
val bind : 'a Promise.promise * 'a -> unit
val predicate : 'a
* ('a * 'a -> bool)
* 'a
* 'a Promise.promise option
* 'a Promise.promise option
-> unit
end
structure Unify :> UNIFY =
struct
open Promise
open Future
exception Unify
val store = ref []
fun addStore(f) = store := f::(!store)
fun clear() = store := [];
fun unify() =
let
fun unifyStore (f::fs) =
let
val _ = f()
in
unifyStore(fs)
end
| unifyStore nil = ()
in
unifyStore(!store)
end
fun bind(px:'a promise, x:'a) =
let
val _ = fulfill(px, x) handle failure => raise Unify
in
unify()
end
fun predicate(
x : 'a,
opn : 'a * 'a -> bool,
y : 'a,
px : 'a promise option,
py : 'a promise option) = (
if (op= = opn)
then addStore(
fn () => (
if (not(isFuture(x)) orelse isFuture(y) orelse not(isSome(px)))
then ()
else bind(valOf(px), y);
if (isFuture(x) orelse not(isFuture(y)) orelse not(isSome(py)))
then ()
else bind(valOf(py), x);
if (isFuture(x) orelse isFuture(y))
then ()
else
if (x = y)
then ( (* can remove this function from store here *) )
else raise Unify))
else addStore(
fn () =>
if (isFuture(x) orelse isFuture(y))
then ()
else
if opn(x, y)
then ( (* can remove this function from store here *) )
else raise Unify))
end
(* example usage of Unify *)
val px = promise();
val x = future(px);
val py = promise();
val y = future(py);
Unify.predicate(x, op=, y, SOME px, SOME py);
Unify.predicate(x, op<, 125, NONE, NONE);
Unify.predicate(x, op>, 100, NONE, NONE);
Unify.predicate(x, op=, y, NONE, NONE);
Unify.bind(px, 123);
Unify.predicate(x, op<>, y, NONE, NONE);
Unify.unify() handle Unify.Unify => Unify.clear();
(* Person datatype used for examples *)
datatype person = Person of {name:string, age:int}
fun unifyPerson(
Person{name=name1, age=age1},
Person{name=name2, age=age2},
pname1,
page1,
pname2,
page2) = (
Unify.predicate(name1, op=, name2, pname1, pname2);
Unify.predicate(age1, op=, age2, page1, page2);
Unify.unify()
);
(* bindings to values *)
val px1 = promise();
val px2 = promise();
val x1 = future(px1);
val x2 = future(px2);
val x = Person{name=x1, age=x2};
val y = x;
(* Unification is symetric *)
val px1 = promise();
val px2 = promise();
val x1 = future(px1);
val x2 = future(px2);
val x = Person{name=x1, age=x2};
unifyPerson(x, Person{name=x1, age=x2}, SOME px1, SOME px2, NONE, NONE);
(* Any two partial values can be unified *)
val px1 = promise();
val x1 = future(px1);
val px2 = promise();
val x2 = future(px2);
unifyPerson(
Person{name=x1, age=x2},
Person{name="George", age=25}, SOME px1, SOME px2, NONE, NONE);
(* If partial values are already equal, then unification does nothing *)
val x = Person{name="George", age=25};
val y = Person{name="George", age=25};
unifyPerson(x, y, NONE, NONE, NONE, NONE);
(* If partial values are incompatible, then they cannot be unified *)
val px1 = promise();
val x1 = future(px1);
unifyPerson(
Person{name=x1, age=26},
Person{name="George", age=25}, SOME px1, NONE, NONE, NONE)
handle Unify.Unify => Unify.clear();
(* Unification is symetric in the arguments *)
val px1 = promise();
val px2 = promise();
val x1 = future(px1);
val x2 = future(px2);
unifyPerson(
Person{name=x1, age=25},
Person{name="George", age=x2}, SOME px1, NONE, NONE, SOME px2);
(* Unification can create cyclic structures *)
datatype person_cyclic = PersonCyclic of {grandfather:person_cyclic}
val px = promise();
val x = future(px);
Unify.bind(px, PersonCyclic{grandfather=x});
(* note: this one was tricksie. the following causes grief. *)
val px = promise();
val x = future(px);
Unify.predicate(x, op=, PersonCyclic{grandfather=x}, SOME px, NONE);
(* Unify.unify(); *) (* uncomment this code see effect *)
Unify.clear();
(* Unification can bind cyclic structures *)
datatype person_cyclic = PersonCyclic of {a:person_cyclic, b:person_cyclic}
val px = promise();
val py = promise();
val x = future(px);
val y = future(py);
Unify.bind(px, PersonCyclic{a=x, b=y});
(* Unify.bind(py, PersonCyclic{a=x, b=y}); *) (* Causes grief in Alice 1.2 *)
(* end first stab at Unify in Alice *)
|