constructor: fun, seq, tree.

checkall(rules) => getenv(rules, inferall(rules, initenv(rules))).

getenv([], env) => [].
getenv([[Def, name, lambda] | t], env) =>
		[nametype | getenv(t, env)]
			where nametype = assoc(env, name).

initenv([]) => [].
initenv([[Def, name, lambda] | t]) =>
		[nametype | initenv(t)]
			where nametype = [name | unknown].

inferall([], env) => env.
inferall([r|t], env) => inferall(t, env2) where
				[type|env2] <- infer(r, env).

gettype(f, env) => type(f) where atom(f) = true.
gettype(f, env) => t where atom(f) = true;  [f|t] = assoc(env, f).
gettype(f, env) => t where [t|env2] <- infer(f, env). 
			/* f is an expression; note that if f returns
			   a new type envt, this won't be propagated */

	/* infer assumes that no free variables occur */

infer([f, e1], env) =>  [out|env1] where 
			fun(in, out) <- gettype(f, env);
			[in|env1] <- infer(e1, env).

infer([Cond, e1, e2, e3], env) =>  [out|env3] where
				[Bool|env1] <- infer(e1,env); 
				[out|env2] <- infer(e2,env1);
				[out|env3] <- infer(e3,env2).

infer([Lambda, x, body], env) => [tf|env2]
		/* assumes all x are distinct */
			where   tf = fun(in, out); 
				env1 =  [[x|in] | env];
				[out|env2] <- infer(body, env1).

infer([Def, f, [Lambda, x, body]], env) => [tf|env2]
			where   tf = fun(in, out); 
				[f | tf] = assoc(env, f);
				[out|env2] <- infer(body, [[x|in] | env]).

infer(f, env)  => [t|env] where 
			atom(f) = true;
			t = type(f).
	/* ordering of these two rules matters */
infer(v, env)  => if null(vt) then [tnew | [[v|tnew]|env]] 
		    else ([t1|env] where [v1|t1] = vt)
		  where 
			atom(v) = true;
			assoc(env, v) = vt.

infer(num, env)  => [Int|env] where numberp(num)=true. 


assoc([], x) 	      => [].
assoc([[a|b]|t], x)   => if eq(a,x) then [a|b] else assoc(t,x). 

/********  Curried Primitive Functions *******/

type(True) => Bool.
type(False) => Bool.

type(Nil)  => seq(x).
type(Null) => fun(seq(x), Bool).
type(Head) => fun(seq(x), x).
type(Tail) => fun(seq(x), seq(x)).
type(Seq) =>  fun(x, fun(seq(x), seq(x))).

type(Atom) => fun(tree(x), Bool).
type(Left) => fun(tree(x), tree(x)).
type(Right) => fun(tree(x), tree(x)).
type(Tree) => fun(tree(x), fun(tree(x), tree(x))).

type(Succ) => fun(Int, Int).
type(Zero) => fun(Int, Bool).
type(Plus) => fun(Int, fun(Int, Int)).
type(Diff) => fun(Int, fun(Int, Int)).
type(Times) => fun(Int, fun(Int, Int)).
type(Div) => fun(Int, fun(Int, Int)).
type(Max) => fun(Int, fun(Int, Int)).

