(*
 *)

(* Safe reduction under lambda *)

val ex1 = <(fn x => x + 1) 6>;

val ex2 = <(fn x => x + 1) (5+1) >;

val ex3 = <(fn f => f 4) (fn x => x + 1)>;

(* safe eta *)

val ex5 =  rev x>;

val ex6 =  (fn x => f x)>;

val ex7 =  append [1] x>;

fun app1 x = append [1] x;

val ex7 =  app1 x>;

(* let hoisting *)

val ex8 = ;            (* Needs Flattening *)
val ex9 = ; (* Needs Unnesting *)
val ex10 = ; (* Needs Hoisting *)


(* use of force *)
val  ex11 =  force  f 4>  x + 1>;

val ex12 = force  xs> <[1,2,3]>;

val ex13 = force  xs> <[]>;

(* **************************************************** *)
(* Recall how monads are defined in MetaML
datatype ('M : * -> * ) Monad = Mon of
    (['a]. 'a -> 'a 'M) * (['a,'b]. 'a 'M -> ('a -> 'b 'M) -> 'b 'M);
*)

(* Think of a simple IO monad with just input and output *)

datatype 'x Io = Io of (string -> ('x * string * string))

fun unit x = (Io (fn input => (x,input,"")));
fun bind (Io f) g = 
   let fun h input = 
           let val (x1,in1,out1) = f input
               val (Io i) = g x1
               val (x2,in2,out2) = i in1
           in (x2,in2,out1^out2) end
   in (Io h) end;
val io = Mon (unit,bind);

(* Now lift this to the meta-level *)
datatype 'x MIo = M of ( -> <('x * char list * char list)>);

fun return x = M(fn s => <(~x,~s,[])>);

val (M f) = return <2>;
val x = <[#"a",#"b",#"c"]>;
fun bind ((M f),g) = 
   let fun h x = 
                       in )
                           in (b,i2,o1 @ o2) end>
                       end) end>
   in M h end;

(* Now some meta-morphisms, I.e. functions with type MIo *)
val getchar =
  let fun h <~x :: ~xs> = <(~x,~xs,[])>
        | h x = <(hd ~x,tl ~x,[])>
  in M h end; 

fun putchar c =
  let fun h xs = <((),~xs,[~c])>
  in M h end;

infix >>=;
val (op >>=) = bind;

val z = getchar >>= (fn c => getchar >>= (fn d => putchar c >>= (fn _ => return d)));

val z2 = 
 bind(getchar,
      fn c => bind(getchar,
                   fn d => bind(putchar c,
                                fn _ => return d)));

fun go (M f) =  ~(f )>;
fun go1 (M f) =  ~(f )>;

fun bind ((M f),g) = 
   let fun h x = 
         force  
                  ~(let val (M h) = g 
                    in force  (b,i2,o1 @ o2)>
                             (h )
                    end)>
              (f x)
   in M h end;


val z3 = 
 bind(getchar,
      fn c => bind(getchar,
                   fn d => bind(putchar c,
                                fn _ => return d)));


(* 
*)