(* CTM Chapter #04 Examples in Alice ML *) (* syntactic sugar for solutions using promises/futures *) open Promise open Future infix 3 ?= val op?= = fulfill val ? = future; (* Unify functions used for examples (via chapter 2) *) exception Unify 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 (* 4.0 Declarative Concurrency *) fun gen (i, n) if (i > n) = nil | gen (i, n) = let val _ = Thread.sleep(Time.fromMilliseconds(Int.toLarge 100)) in i::gen(i+1, n) end val xs = gen(1, 10); inspect (map (fn x => x*x) xs); fun gen (i, n, p) if (i > n) = ( p ?= nil; future p ) | gen (i, n, p) = let val px = promise() in Thread.sleep(Time.fromMilliseconds(Int.toLarge 100)); p ?= i::(future px); gen(i+1, n, px); future p end val xs = promise(); spawn gen(1, 10, xs); (* note that map will run concurrently but won't return a result until the function terminates *) val ys = spawn map (fn x => x*x) (future xs); inspect ys; fun mapPromise f nil p = ( p ?= nil; future p ) | mapPromise f (x::xs) p = let val px = promise() in p ?= (f x)::(future px); mapPromise f xs px; future p end; val ys = promise(); spawn mapPromise (fn x => x*x) (future xs) ys; inspect (future ys); (* 4.1.3 The data-driven concurrent model - Semantics of threads *) let val b = promise() in spawn b ?= true; if (future b) then inspect "yes" else () end; (* 4.1.4 The data-driven concurrent model - What is declarative concurrency *) fun double (nil, p) = ( p ?= nil; future p ) | double (x::xs, p) = let val px = promise() in p ?= (2*x)::(future px); double(xs, px); future p end val xs = promise() val ys = promise() val xr = promise(); spawn double(future xs, ys); inspect (future ys); xs ?= 1::2::3::(future xr); val x = promise() val y = promise(); unifyPromise unifySimple (x, known(1)); unifyPromise unifySimple (y, x); val x = promise() val y = promise(); unifyPromise unifySimple (y, x); unifyPromise unifySimple (x, known(1)); datatype 'a foo = Foo of 'a * 'a val y = promise() val z = promise() val w = promise() val x = Foo (future y, future w); unifyPromise unifySimple (y, z); val y = promise() val z = promise() val w = promise() val x = Foo (future z, future w); unifyPromise unifySimple (y, z); val x = promise() val y = promise(); spawn unifyPromise unifySimple (x, known(1)); spawn unifyPromise unifySimple (y, known(2)); spawn unifyPromise unifySimple (x, y); (future x = future y) handle Unify => false; let datatype status = Ok | Err val x = promise() val y = promise() val x1 = promise() val y1 = promise() val s1 = promise() val s2 = promise() val s3 = promise() in spawn ( unifyPromise unifySimple (x1, known(1)); s1 ?= Ok ) handle Unify => s1 ?= Err; spawn ( unifyPromise unifySimple (y1, known(2)); s2 ?= Ok ) handle Unify => s2 ?= Err; spawn ( unifyPromise unifySimple (x1, y1); s3 ?= Ok ) handle Unify => s3 ?= Err; if (future s1 = Err) orelse (future s2 = Err) orelse (future s3 = Err) then ( x ?= 1; y ?= 1 ) else ( x ?= future x1; y ?= future y1 ) end; (* 4.2.1 Basic thread programming techniques - Creating threads *) spawn let fun count n = if (n > 0) then count(n-1) else () in count 1000000 end; val x = (spawn 10 * 10) + 100*100; let val x = promise() val y = spawn 10*10 in x ?= y + 100*100 end; (* 4.2.2 Basic thread programming techniques - Threads and the browser *) spawn inspect 111; inspect 222; let val x1 = promise() val x2 = promise() val y1 = promise() val y2 = promise() in spawn inspect (future x1); spawn inspect (future y1); spawn x1 ?= "all"::"roads"::(future x2); spawn y1 ?= "all"::"roams"::(future y2); spawn x2 ?= "lead"::"to"::"rome"::(future (promise())); spawn y2 ?= "lead"::"to"::"rhodes"::(future (promise())) end; (* 4.2.3 Basic thread programming techniques - Dataflow computation with threads *) (* simple dataflow behavior *) let val x0 = promise() val x1 = promise() val x2 = promise() val x3 = promise() in spawn let val y0 = promise() val y1 = promise() val y2 = promise() val y3 = promise() in inspect (future y0, future y1, future y2, future y3); y0 ?= (future x0) + 1; y1 ?= (future x1) + (future y0); y2 ?= (future x2) + (future y1); y3 ?= (future x3) + (future y2); inspect "completed" end; inspect (future x0, future x1, future x2, future x3); x0 ?= 0; x1 ?= 1; x2 ?= 2; x3 ?= 3 end; (* using a declarative program in a concurrent setting *) fun forall nil f = () | forall (x::xs) f = (f x; forall xs f); let val xs = promise() val x1 = promise() val x2 = promise() in spawn forall (future xs) inspect; spawn xs ?= 1::(future x1); spawn x1 ?= 2::3::(future x2); spawn x2 ?= 4::nil end; let val xs = promise() in spawn forall (future xs) inspect; xs ?= [1,2,3,4] end; (* A concurrent map function *) fun mapPromiseSpawn f nil p = ( p ?= nil; future p ) | mapPromiseSpawn f (x::xs) p = let val px = promise() in p ?= (spawn (f x))::(future px); mapPromiseSpawn f xs px; future p end; let val p = promise() val xs = promise() val ys = promise() val zs = promise() val f = promise() in spawn mapPromiseSpawn (future f) (future xs) p; inspect (future p); xs ?= 1::2::(future ys); f ?= (fn x => x*x); ys ?= 3::(future zs); zs ?= nil end; (* A concurrent Fibonacci function *) fun fib x if (x <= 2) = 1 | fib x = (spawn fib (x-1)) + (fib (x-2)); inspect (fib 26); (* Note: All Alice threads run at the same priority *) (* 4.3.1 Streams - Basic producer/consumer *) val xs = promise() val xs2 = promise(); xs ?= 0::1::2::3::4::(future xs2); val xs3 = promise(); xs2 ?= 5::6::7::(future xs3); fun generate (n, limit, p) if (n >= limit) = ( p ?= nil; future p ) | generate (n, limit, p) = let val px = promise() in p ?= n::(future px); generate(n+1, limit, px); future p end fun sum (nil, a) = a | sum (x::xs, a) = sum(xs, a+x); (* Note: I'm using smaller limit so as to fit in 31 bit int *) (* Convert functions to IntInf if you want to use 150000 *) let val xs = promise() val s = promise() in spawn generate(0, 15000, xs); spawn s ?= sum(future xs, 0); inspect (future s) end; let val xs = promise() val s = promise() in spawn generate(0, 15000, xs); spawn s ?= foldl (fn (x, y) => x+y) 0 (future xs); inspect (future s) end; let val xs = promise() val s1 = promise() val s2 = promise() val s3 = promise() in spawn generate(0, 15000, xs); spawn s1 ?= sum(future xs, 0); spawn s2 ?= sum(future xs, 0); spawn s3 ?= sum(future xs, 0); inspect (future s1, future s2, future s3) end; (* 4.3.2 Streams - Transducers and pipelines *) (* filtering a stream *) fun filterPromise f nil p = ( p ?= nil; future p ) | filterPromise f (x::xs) p = if (f x) then let val px = promise() in p ?= x::(future px); filterPromise f xs px; future p end else (filterPromise f xs p) fun isOdd x = (x mod 2) <> 0; let val xs = promise() val ys = promise() val s = promise() in spawn generate(0, 15000, xs); spawn filterPromise isOdd (future xs) ys; spawn s ?= sum(future xs, 0); inspect (future s) end; (* Sieve of Eratosthenes *) fun sieve (nil, p) = ( p ?= nil; future p ) | sieve (x::xs, p) = let val ys = promise() val px = promise() in p ?= x::(future px); spawn ys ?= List.filter (fn y => (y mod x) <> 0) xs; sieve(future ys, px); future p end; (* Note: Not sure it works for 100000 - takes a while *) let val xs = promise() val ys = promise() in spawn generate(2, 10000, xs); spawn sieve(future xs, ys); inspect (future ys) end; fun sieve (nil, m, p) = ( p ?= nil; future p ) | sieve (x::xs, m, p) = let val ys = promise() val px = promise() in p ?= x::(future px); if (x <= m) then spawn ys ?= List.filter (fn y => (y mod x) <> 0) xs else ys ?= xs; sieve(future ys, m, px); future p end; (* 4.3.3 Streams - Managing resources and improving throughput *) (* Flow control with demand driven concurrency *) fun dgenerate (n, nil) = () | dgenerate (n, x::xs) = ( x ?= n; dgenerate(n+1, xs) ) fun dsum (xs, a, limit) if (limit <= 0) = ( xs ?= nil; a ) | dsum (xs, a, limit) = let val x = promise() val xr = promise() in xs ?= x::(future xr); dsum(xr, a+(future x), limit-1) end; let val xs = promise() val s = promise() in spawn dgenerate(0, future xs); spawn s ?= dsum(xs, 0, 15000); inspect (future s) end; (* Flow control with a bounded buffer *) fun buffer (n, xs, ys) = let fun startup (n, xs) if (n = 0) = xs | startup (n, xs) = let val x = promise() val xr = promise() in xs ?= x::(future xr); startup(n-1, xr) end val endb = startup(n, xs) fun askLoop (y::ys, x::xs, endb) = let val end2 = promise() in y ?= future x; endb ?= promise()::(future end2); askLoop(ys, xs, end2) end | askLoop (_, _, _) = () in spawn askLoop(ys, future xs, endb) end; let val xs = promise() val ys = promise() val s = promise() in spawn dgenerate(0, future xs); spawn buffer(4, xs, future ys); spawn s ?= dsum(ys, 0, 15000); inspect (future s) end; (* Flow control with thread priorities - priorities not supported by Alice *) fun setThisPriority () = (); let val xs = promise() val s = promise() in spawn ( setThisPriority(); dgenerate(0, future xs) ); spawn ( setThisPriority(); s ?= dsum(xs, 0, 15000) ); inspect (future s) end; (* 4.3.4 Streams - Stream objects *) fun nextState (m, x1, n, x2) = () fun streamObject (nil, x1, t1) = ( t1 ?= nil; future t1 ) | streamObject (m::s2, x1, t1) = let val n = promise() val t2 = promise() val x2 = promise() in nextState(m, x1, n, x2); t1 ?= (future n)::(future t2); streamObject(s2, future x2, t2); future t1 end; let val s0 = promise() val x0 = promise() val t0 = promise() in spawn streamObject(future s0, future x0, t0) end; let val s0 = promise() val t0 = promise() val u0 = promise() val v0 = promise() in spawn streamObject(future s0, 0, t0); spawn streamObject(future t0, 0, u0); spawn streamObject(future u0, 0, v0) end; (* 4.3.5.1 Streams - Digital logic simulation - Combinatorial logic *) fun notGate (nil, p) = ( p ?= nil; future p ) | notGate (x::xs, p) = let val px = promise() in p ?= (1-x)::(future px); notGate(xs, px); future p end fun notG (xs, p) = let fun notLoop (nil, p) = ( p ?= nil; future p ) | notLoop (x::xs, p) = let val px = promise() in p ?= (1-x)::(future px); notLoop(xs, px); future p end in spawn notLoop(xs, p) end fun gateMaker f = fn (xs, ys, p) => let fun gateLoop (nil, _, p) = ( p ?= nil; future p ) | gateLoop (_, nil, p) = ( p ?= nil; future p ) | gateLoop (x::xr, y::yr, p) = let val px = promise() in p ?= f(x, y)::(future px); gateLoop(xr, yr, px); future p end in spawn gateLoop(xs, ys, p) end val andG = gateMaker (fn (x, y) => x*y) val orG = gateMaker (fn (x, y) => x+y-x*y) val nandG = gateMaker (fn (x, y) => 1-x*y) val norG = gateMaker (fn (x, y) => 1-x-y+x*y) val xorG = gateMaker (fn (x, y) => x+y-2*x*y) fun fullAdder (x, y, z, c, s) = let val k = promise() val l = promise() val m = promise() val pc = promise() val ps = promise() in andG(x, y, k); andG(y, z, l); andG(x, z, m); orG(future l, future m, pc); orG(future k, future pc, c); xorG(x, y, ps); xorG(z, future ps, s); future s end; let val x = [1, 1, 0, future (promise())] val y = [0, 1, 0, future (promise())] val z = [1, 1, 1, future (promise())] val c = promise() val s = promise() in fullAdder(x, y, z, c, s); inspect ((x, y, x), (future c, future s)) end; (* 4.3.5.2 Streams - Digital logic simulation - Sequential logic *) fun delayG xs = 0::xs fun latch (c, di, p) = let val d0 = promise() val x = promise() val y = promise() val z = promise() val f = promise() in p ?= future d0; andG(future f, c, x); notG(c, z); andG(future z, di, y); orG(future x, future y, d0); f ?= delayG(future d0); future d0 end (* 4.3.5.3 Streams - Digital logic simulation - Clocking *) fun clock p = let fun loop b = let val px = promise() in p ?= b::future px; loop b; future p end in spawn (loop 1) end fun clock p = let fun loop b = let val px = promise() in Thread.sleep(Time.fromMilliseconds(Int.toLarge(1000))); p ?= b::future px; loop b; future p end in spawn (loop 1) end (* 4.3.5.4 Streams - Digital logic simulation - A linguistic abstraction for logic gates *) (* Note: Not willing to tackle this at the current time *) (* 4.4.1 Using the declarative concurrent model directly - Order-determining concurrency *) datatype ('a, 'b, 'c) obtree = Leaf | Tree of { key : 'a, value : 'b, x : 'c promise, y : 'c promise, left : ('a, 'b, 'c) obtree, right : ('a, 'b, 'c) obtree } val scale = 30 fun depthFirst (Tree{x, y, left=Leaf, right=Leaf, ...}, level, leftLim, rootX, rightLim) = let in x ?= future leftLim; rootX ?= future x; rightLim ?= future x; spawn y ?= scale * level end | depthFirst (Tree{x, y, left, right=Leaf, ...}, level, leftLim, rootX, rightLim) = let in x ?= future rootX; spawn y ?= scale * level; depthFirst(left, level+1, leftLim, rootX, rightLim) end | depthFirst (Tree{x, y, left=Leaf, right, ...}, level, leftLim, rootX, rightLim) = let in x ?= future rootX; spawn y ?= scale * level; depthFirst(right, level+1, leftLim, rootX, rightLim) end | depthFirst (Tree{x, y, left, right, ...}, level, leftLim, rootX, rightLim) = let val lRootX = promise() val rRootX = promise() val lRightLim = promise() val rLeftLim = promise() in rootX ?= future x; spawn x ?= (future lRootX + future rRootX) div 2; spawn y ?= scale * level; spawn rLeftLim ?= (future lRightLim) + scale; depthFirst(left, level+1, leftLim, lRootX, lRightLim); depthFirst(right, level+1, rLeftLim, rRootX, rightLim) end | depthFirst (Leaf, _, _, _, _) = (); (* 4.4.2 Using the declarative concurrent model directly - Coroutines *) fun fspawn p = let val pid = promise() in spawn ( pid ?= Thread.current(); Thread.suspend(future pid); future p); future pid end fun fresume id = ( Thread.resume id; Thread.suspend(Thread.current())) (* 4.4.3 Using the declarative concurrent model directly - Concurrent composition *) fun barrier ps = let fun barrierLoop (nil, n) = n | barrierLoop (f::fs, n) = let val m = promise() in spawn ( f(); m ?= n ); barrierLoop(fs, future m) end val s = promise() in spawn s ?= barrierLoop(ps, ()); await s end (* 4.5 Lazy execution *) fun lazy f1 x = 1+x*(3+x*(3+x)) fun lazy f2 x = let val y = x*x in y*y end fun lazy f3 x = (x+1)*(x+1) val a = f1(10) val b = f2(20) val c = f3(30) val d = a+b (* 4.5.1 Lazy execution - The demand-driven concurrent model *) val y = promise(); (fn a => a ?= (lazy 111 * 111)) y; inspect (future y); await (future y); let val x = promise() val y = promise() val z = promise() in spawn x ?= (lazy (fn () => 3) ()); spawn y ?= (lazy (fn () => 4) ()); spawn z ?= (future x) + (future y) end; let val x = promise() val z = promise() in let val f = (fn () => 3) () val t1 = spawn unifyPromise unifySimple (x, lazy known(f)) val t2 = spawn unifyPromise unifySimple (x, lazy known(2)) val t3 = spawn z ?= (future x) + 4 in await t1; await t2; await t3 end; inspect "Unify" end handle Unify => inspect "Unify Exception"; let val x = promise() val y = promise() val z = promise() val f = (fn () => 3) () in spawn unifyPromise unifySimple (x, lazy known(f)); spawn unifyPromise unifySimple (x, y); spawn (if future x = future y then z ?= 10 else ()); inspect (future x) end; fun lazy generate n = n::generate(n+1) val x = generate 0; inspect x; inspect (hd (tl (tl x))); fun generate n = lazy(n::generate(n+1)) (* 4.5.2 Lazy execution - Declarative computation models *) val x = 11*11 (* (1)&(2)&(3) together *) fun lazy lazyMul (a, b) = a*b val x = lazyMul(11, 11); (* (1)&(2) together *) await x; (* (3) seperate *) val x = lazy 11 * 11; (* (1)&(2) together *) await x; (* (3) seperate *) val x = promise(); (* (1) seperate *) x ?= 11 * 11; (* (2)&(3) together *) val x = promise(); (* (1) seperate *) spawn x ?= 11 * 11; (* (2)&(3) together *) spawn if (future x > 100) then inspect "big" else (); val x = promise(); (* (1) seperate *) x ?= (lazy 11 * 11); (* (2) seperate *) await x; (* (3) seperate *) val x = promise(); (* (1) seperate *) spawn x ?= (lazy 11 * 11); (* (2) seperate *) spawn await x; (* (3) seperate *) let val z = promise() fun lazy f1 x = x + future z fun lazy f2 y = (z ?= 1; y + future z) in (* inspect (f1(1) + f2(2)) *) (* deadlock *) inspect (f2(1) + f1(2)) end; (* 4.5.3 Lazy execution - Lazy streams *) fun lazy generate n = n::generate(n+1) fun sum (_, a, limit) if (limit <= 0) = a | sum (x::xs, a, limit) = sum(xs, a+x, limit-1) | sum (nil, _, _) = raise Empty; let val xs = generate 0 val s = sum(xs, 0, 15000) in inspect s end; let val s1 = promise() val s2 = promise() val s3 = promise() val xs = generate 0 in s1 ?= (spawn sum(xs, 0, 15000)); s2 ?= (spawn sum(xs, 0, 10000)); s3 ?= (spawn sum(xs, 0, 5000)) end; (* 4.5.4 Lazy execution - Bounded buffer *) fun buffer1 (xin, n) = let val xend = List.drop(xin, n) fun lazy loop (x::xs, xend) = x::loop(xs, tl xend) | loop (nil, xend) = raise Empty in loop(xin, xend) end fun buffer2 (xin, n) = let val xend = List.drop(xin, n) fun lazy loop (x::xs, xend) = x::loop(xs, spawn tl xend) | loop (nil, xend) = raise Empty in loop(xin, xend) end fun lazy ints n = let val _ = Thread.sleep(Time.fromMilliseconds(Int.toLarge 1000)) in n::ints(n+1) end; let val xin = ints 1 val out = buffer2(xin, 5) in inspect out; inspect (hd out); inspect (tl(tl(tl(tl(tl(tl(tl(tl(tl(tl out)))))))))) end; (* 4.5.5 Lazy execution - Reading a file lazily *) fun readListLazy fname = let val infile = TextIO.openIn fname fun lazy readNext () = let val xs = explode(TextIO.inputN(infile, 100)) in if (null xs) then TextIO.closeIn infile else ( readNext(); () ); xs end in readNext() end (* 4.5.6 Lazy execution - The Hamming problem *) fun lazy times n [] = [] | times n (x::xs) = (n*x)::(times n xs) fun lazy merge xs nil = xs | merge nil ys = ys | merge (xs as x::xr) (ys as y::yr) = if x < y then x::(merge xr ys) else if x > y then y::(merge xs yr) else x::(merge xr yr) val h = promise(); h ?= 1 :: (merge (times 2 (future h)) (merge (times 3 (future h)) (times 5 (future h)))); inspect h; fun touch (n, x::xs) if (n > 0) = touch(n-1, xs) | touch (_, _) = (); touch(20-1, future h); (* alternately, we could use take to accomplish the same end *) List.take(future h, 20); (* 4.5.7 Lazy execution - Lazy list operations *) (* lazy append *) fun lazy lazyAppend (nil, ys) = ys | lazyAppend (x::xs, ys) = x::lazyAppend(xs, ys) val x = lazyAppend(explode "foo", explode "bar") fun lazy makeLazy nil = nil | makeLazy (x::xs) = x::makeLazy xs val x = lazyAppend(explode "foo", makeLazy(explode "bar")); inspect (implode x); (* lazy mapping *) fun lazy lazyMap f nil = nil | lazyMap f (x::xs) = (f x)::(lazyMap f xs) (* lazy integer lists *) datatype 'a lazyfromtype = Infinite | Number of 'a fun lazyFrom (i, j) = let fun lazy lazyFromLoop (i, j) if (i > j) = nil | lazyFromLoop (i, j) = i::lazyFromLoop(i+1, j) fun lazy lazyFromInf i = i::lazyFromInf(i+1) in case j of Infinite => lazyFromInf i | Number x => lazyFromLoop(i, x) end (* lazy flatten *) datatype 'a nestedlist = Leaf of 'a | Branch of 'a nestedlist list fun lazyFlatten xs = let fun lazy lazyFlattenD (Branch nil, e) = e | lazyFlattenD (Branch ((Branch x)::xr), e) = lazyFlattenD(Branch x, lazyFlattenD(Branch xr, e)) | lazyFlattenD (Branch ((Leaf x)::xr), e) = x::lazyFlattenD(Branch xr, e) | lazyFlattenD (Leaf x, e) = x::e in lazyFlattenD(xs, []) end (* lazy reverse *) fun lazyReverse s = let fun lazy rev (nil, r) = r | rev (x::xs, r) = rev(xs, x::r) in rev(s, nil) end val x = lazyReverse [#"a", #"b", #"c"]; inspect x; List.take(x, 1); (* lazy filter *) fun lazy lazyFilter f nil = nil | lazyFilter f (x::xs) = if (f x) then x::(lazyFilter f xs) else (lazyFilter f xs) (* 4.5.8.1 Lazy execution - Persistent queues and algorithm design - Amortized persistent queue *) datatype 'a queue = Queue of int * 'a list * int * 'a list fun newQueue () = Queue(0, nil, 0, nil) fun checkQ (q as Queue(lenf, f, lenr, r)) = if (lenf >= lenr) then q else Queue(lenf+lenr, lazyAppend(f, List.rev r), 0, nil) fun insertQ (Queue(lenf, f, lenr, r), x) = checkQ(Queue(lenf, f, lenr+1, x::r)) fun deleteQ (q, px) = let val Queue(lenf, f, lenr, r)= checkQ(q) in px ?= hd f; checkQ(Queue(lenf-1, tl f, lenr, r)) end (* 4.5.8.2 Lazy execution - Persistent queues and algorithm design - Worst-case persistent queue *) fun reverse r = let fun rev (nil, a) = a | rev (x::xs, a) = rev(xs, x::a) in rev(r, nil) end fun lazy lazyAppend (nil, b) = b | lazyAppend (x::xs, b) = x::lazyAppend(xs, b) fun lazy lazyAppRev (nil, y::nil, b) = y::b | lazyAppRev (x::xs, y::ys, b) = x::lazyAppRev(xs, ys, y::b) | lazyAppRev (x::xs, nil, b) = raise Empty fun checkQ (q as Queue(lenf, f, lenr, r)) = if (lenf >= lenr) then q else Queue(lenf+lenr, lazyAppRev(f, r, nil), 0, nil) (* 4.5.9 Lazy execution - List comprehensions *) (* Note: Alice doesn't sport list comprehensions *) fun eagerFrom (i, j) = let fun fromLoop (i, j) if (i > j) = nil | fromLoop (i, j) = i::fromLoop(i+1, j) fun fromInf i = i::fromInf(i+1) in case j of Infinite => fromInf i | Number x => fromLoop(i, x) end val z = map (fn x =>(x, x)) (eagerFrom(1, Number 10)) val z = lazyMap (fn x =>(x, x)) (lazyFrom(1, Number 10)) val z = lazyFlatten (Branch ( lazyMap (fn x => Branch (lazyMap (fn y => Leaf (x, y)) (lazyFrom(1, Number x)))) (lazyFrom(1, Number 10)))) fun fmap f xs = lazyFlatten(Branch (lazyMap f xs)) val z = fmap (fn x => Branch (lazyMap (fn y => Leaf (x, y)) (lazyFrom(1, Number x)))) (lazyFrom(1, Number 10)) val z = lazyFilter (fn (x,y) => x+y <= 10) (fmap (fn x => Branch (lazyMap (fn y => Leaf (x, y)) (lazyFrom(1, Number x)))) (lazyFrom(1, Number 10))) val z = fmap (fn x => Branch (lazyMap (fn y => Leaf (x, y)) (lazyFrom(1, Number (10-x))))) (lazyFrom(1, Number 10)) (* 4.6.1 Soft real-time programming - Basic operations *) fun for a b s f = let fun loopup c if (c <= b) = (f c; loopup (c+s)) | loopup c = () fun loopdown c if (c >= b) = (f c; loopdown (c+s)) | loopdown c = () in if (s > 0) then loopup a else if (s < 0) then loopdown a else () end; let fun ping n = if (n = 0) then inspect "ping terminated" else ( Thread.sleep(Time.fromMilliseconds(Int.toLarge 500)); inspect "ping"; ping(n-1)) fun pong n = ( for 1 n 1 (fn i => ( Thread.sleep(Time.fromMilliseconds(Int.toLarge 600)); inspect "pong" )); inspect "pong terminated") in inspect "game started"; spawn ping 5; spawn pong 5 end; functor PingPong () = struct fun ping n = if (n = 0) then inspect "ping terminated" else ( Thread.sleep(Time.fromMilliseconds(Int.toLarge 500)); inspect "ping"; ping(n-1)) fun pong n = ( for 1 n 1 (fn i => ( Thread.sleep(Time.fromMilliseconds(Int.toLarge 600)); inspect "pong" )); inspect "pong terminated") fun run () = let val _ = inspect "game started" val t1 = spawn ping 50 val t2 = spawn pong 50 in await t1; await t2; OS.Process.exit OS.Process.success end val _ = run() end (* 4.6.2 Soft real-time programming - Ticking *) fun forall nil f = () | forall (x::xs) f = (f x; forall xs f) fun newTicker p = let fun loop p = let val x = Time.now() val px = promise() in Thread.sleep(Time.fromMilliseconds(Int.toLarge 1000)); p ?= x::(future px); loop px end in spawn loop p end; val px = promise(); (* spawn newTicker px; *) (* Uncomment to run *) spawn forall (future px) (fn x => inspect x); fun newTicker p = let fun loop (p, t) = let val x = Time.now() val px = promise() in Thread.sleep(Time.fromMilliseconds(Int.toLarge 900)); if (Time.toSeconds(t) <> Time.toSeconds(x)) then p ?= x::(future px) else (); loop(px, x) end in spawn loop(p, Time.now()) end fun newTicker p = let fun loop (p, t) = let val x = Time.now() val px = promise() in if (Time.toSeconds(t) > Time.toSeconds(x)) then Thread.sleep(Time.fromMilliseconds(Int.toLarge 900)) else if (Time.toSeconds(t) > Time.toSeconds(x)) then Thread.sleep(Time.fromMilliseconds(Int.toLarge 1100)) else Thread.sleep(Time.fromMilliseconds(Int.toLarge 1000)); p ?= x::(future px); loop(px, x) end in spawn loop(p, Time.now()) end (* 4.7 The Haskell language *) fun lazy factorial 0 : int = 1 : int | factorial n if (n > 0) = n * factorial(n-1) | factorial n = raise Domain (* 4.7.1 The Haskell language - Computation model *) val n = 3; (if n >= 0 then factorial else raise Domain) (factorial(factorial n)); (* 4.7.2 The Haskell language - Lazy evaluation *) fun lazy dropWhile f nil = nil | dropWhile f (x::xs) = if (f x) then dropWhile f xs else x::xs fun lazy dropUntil f xs = dropWhile (not o f) xs fun lazy sqrt' x = let fun lazy goodEnough guess = (abs(x - guess*guess) / x) < 0.00001 fun lazy improve guess = (guess + x/guess) / 2.0 val sqrtGuesses = promise() in sqrtGuesses ?= 1.0::(lazyMap improve (future sqrtGuesses)); hd (dropUntil goodEnough (future sqrtGuesses)) end; (sqrt' 36.0) + 0.0; (* 4.7.3 The Haskell language - Currying *) val doubleList = lazyMap (fn x => 2*x); val x = doubleList [1,2,3,4]; List.take(x, 4); (* 4.7.4 The Haskell language - Polymorphic types *) datatype 'a btree = Leaf | Node of 'a * 'a btree * 'a btree fun lazy bsize Leaf = 0 | bsize (Node((k, v), lt, rt)) = 1 + (bsize lt) + (bsize rt) fun lazy lookup k Leaf = NONE | lookup k (Node((nk, nv), lt, rt)) if (k < nk) = lookup k lt | lookup k (Node((nk, nv), lt, rt)) if (k > nk) = lookup k rt | lookup k (Node((nk, nv), lt, rt)) = SOME nv (* 4.8.3.2 Limitations and Extensions of declarative programming - Nondeterminism *) fun skip xs = if (isDetermined xs) then case xs of x::xr => skip xr | nil => nil else xs fun skip1 nil = nil | skip1 (xs as x::xr) = if (isDetermined xr) then skip1 xr else xs fun displayFrame x = () fun display xs = case skip1 xs of x::xr => ( displayFrame x; display xr ) | nil => () (* 4.9.1 Advanced topics - The declarativwe concurrent model with exceptions *) fun foo x = x val x = lazy fn x => let val a = promise() val b = promise() in a ?= foo(1); b ?= foo(2); a ?= future b; a end val x = ( raise Promise; () ) handle Promise => () fun byneed2 f x = lazy ( fn x => let val y = promise() in f y; x ?= future y; x end handle exc => raise Promise) (* 4.9.3 Advanced topics - Dataflow variables as communication channels *) fun syncSend x m = let val sync = promise() in lazy fn x => ( x ?= future m; sync ?= (); future x); await sync end (* 4.9.5 Advanced topics - Usefulness of dataflow variables *) fun xfuture x = ( x = promise(); future x ) |