(* credits goes to M. Lemay *)
#include "share/atspre_staload.hats"
#include "share/HATS/atspre_staload_libats_ML.hats"
// we always need this to make c happy
implement main0 () = ()
// lazyness means we only compute values when we need them
// and save them so they don't need to be recomputed
// functions that don't take parameters
// let you schedule computations for later
typedef BadLazy (A) = cfun (A)
// for instance:
fun slowComp ():<cloref1> int =
let val () = println!("long time in a closure ...")
in 1+1 end
// we can evaluate the lazy value with ()
val i = slowComp ()
val () = println! i
// we can pass around BadLazy values
fun decide (bl: BadLazy (int)):int =
if true // complicated logic
then bl() + 1 // only compute the value when needed
else 4
val () = println! (decide (slowComp))
val () = println! ()
// however the result is not saved and must be recomputed
// this is solved with the $delay keyword and lazy type
val slowComp2: lazy(int) =
$delay(
let val () = println!("long time in a $delay ...")
in 1+2 end
)
val i = !slowComp2
val () = println! i
val i = !slowComp2 // the value is not recomputed
val () = println! i
fun decide (bl: lazy(int)): int =
if true // complicated logic
then !bl + 1 // only compute the value when needed,
// or use the one that was already computed
else 4
val i = decide (slowComp2) // the value is not recomputed
val () = println! i
val () = println! ()
// remember that types can interact
val x1: lazy (lazy (int)) =
$delay( $delay(
let val () = println!("Hello from inside $delay $delay ...")
in 999 end
))
// We need the exact amount of ! to get the value.
// Too many or too few will result in a type error.
val () = println! (!(!x1))
val () = println! ()
// we can use lazyness to build a lazy list (normally called a stream)
// one way to do this is
datatype mystream (A:t@ype) =
| mystream_nil of ()
| mystream_cons of (A, lazy (mystream (A)))
// but this stream requires the first value to be computed,
// so we can make a lazier stream as they are defined in ATS
datatype mystream_con (A:t@ype) =
| mystream_nil of ()
| mystream_cons of (A, mystream (A))
where mystream (A:t@ype) = lazy (mystream_con (A)) // just a mutual typedef
// now we don't even know if a stream is empty until we unpack it
// we can build streams using the constructors
// (but we need to delay the right things)
val ls:mystream (int) = $delay (mystream_cons (1, $delay (mystream_nil ())))
// we can pattern match as long as we unpack the correct values
val a =
case+ !ls of //NOTE: we must force computation using ! before pattern matching
| mystream_nil () => 100
| mystream_cons (x, _) => x
val () = println! ("Pattern matching the first element ...")
val () = println! (a:int) // 1
val () = println! ()
// we can create a large list
fun longg (i: int): mystream (int) =
$delay(
if i=0
then mystream_nil()
else let val () = println!("a long time in longg ...")
in mystream_cons (i, longg (i-1)) end
)
// equivelently:
fun longgg (i: int): mystream (int) =
if i=0
then $delay (mystream_nil())
else let val () = println!("a long time in g longgg ...")
in $delay (mystream_cons (i, longgg (i-1))) end
//we can print just the first several values we are intrested in
fun myprint (ls: mystream (int), i: int): void =
if i <= 0
then println! ("...")
else case+ !ls of
| mystream_nil() => ()
| mystream_cons(x, rest) =>
let val () = println!(x)
in myprint(rest,i-1) end
val () = myprint(longg (10000), 10) // only 10 "hard computations" are done
val () = println!()
// perhaps the most intresting thing about streams is that they can modle infinite data
// and infinite stream of 42s
val fortytwo =
let
fun fortytwos (): mystream (int) =
$delay(mystream_cons(42, fortytwos()))
in
fortytwos()
end
val () = myprint (fortytwo,10)
val () = println!()
// and infinite stream of fibs
val fibs =
let
fun help(i:int,j:int):mystream(int) =
$delay(mystream_cons(i, help(j, i+j)))
in
help(0,1)
end
val () = myprint (fibs,20)
val () = println!()
// and infinite stream of positive numbers
val pos =
let
fun help(i:int):mystream(int) =
$delay(
mystream_cons(i, help(i+1) ) //note that the recursion help(i+1) is ok becuase of lazyness!
)
in
help(1)
end
val () = myprint (pos,100)
val () = println!()
// we can implement several combinators on streams
fun {A,B:t@ype} mystreammap(s:mystream(A), f:cfun(A,B)): mystream(B) =
$delay(
case+ !s of
| mystream_nil() => mystream_nil()
| mystream_cons(x, rest) => mystream_cons(f(x), mystreammap(rest, f))
)
fun {A:t@ype} mystreamfilter(s:mystream(A), f:cfun(A,bool)): mystream(A) =
$delay(
case+ !s of
| mystream_nil() => mystream_nil()
| mystream_cons(x, rest) =>
if f(x)
then mystream_cons(x, mystreamfilter(rest,f) )
else !(mystreamfilter(rest,f))
)
//we do have to be careful when using infinite data, this is ok:
val () = myprint(mystreamfilter(pos, (lam(x) => x < 10) ), 5 )
//but this will crash
// val () = myprint(mystreamfilter(pos, (lam(x) => x < 10) ), 15 )
//becuase there is no way for the program to know that there is another x less then 10 without doing infinite work
val () = println!()
// an infinite stream of all the numbers greater then or equal to 2
val from2 =
let
fun help(i:int):mystream(int) =
$delay(
mystream_cons(i, help(i+1) )
)
in
help(2)
end
// an infinite stream of all the primes
val primes =
let
fun sieve (s: mystream (int)): mystream (int) =
$delay(
case+ !s of
| mystream_nil () => mystream_nil () // this never actual happens becuase the stream is infinite
| mystream_cons (prime, rest) =>
mystream_cons (prime,
sieve (mystreamfilter (rest, lam (x) => x % prime <> 0)))
)
in
sieve (from2)
end
val () = myprint (primes, 100 )
val () = println!()
//we can write combinators over streams
fun {A:t@ype} mystreamappend(l:mystream(A), r:mystream(A)): mystream(A) =
$delay(
case+ !l of
| mystream_nil() => !r
| mystream_cons(x, rest) => mystream_cons(x, mystreamappend(rest,r) )
)
fun {A,B:t@ype} mystreamzip(l:mystream(A), r:mystream(B)): mystream($tup(A,B)) =
$delay(
case+ (!l , !r) of
| (mystream_nil() , _ ) => mystream_nil()
| ( _ , mystream_nil() ) => mystream_nil()
| (mystream_cons(lx,lrest), mystream_cons(rx,rrest)) => mystream_cons($tup(lx,rx), mystreamzip(lrest, rrest) )
)
// once we have good library of combinators we can do lots of intresting things with infinite data