(* First, some well known code.  These actually redefine 
   existing predicates in SWI-Prolog with the same meaning. 
*)

memb(X,[X|L]) :- !.
memb(X,[_|L]) :- memb(X,L).
append([],K,K).
append([X|L],K,[X|M]) :- append(L,K,M).

(* Bubble sort.  Notice the essential use of cut in this 
   program.  Sorted list are increasing lists of integers.
*)

bsort(L1,L2) :- 
       append(Sorted,[Big,Small|Rest],L1),
       Small < Big, !,
       append(Sorted,[Small,Big|Rest],BetterL1),
       bsort(BetterL1,L2).
bsort(L1,L1).

(* The following illustrates a Prolog implementation of a type 
   inference predicate for a small subset of a functional 
   language like ML.
*)

example(1, apply(apply(plus,1),2)).

example(2, lam(x,x)).

example(3, lam(x, apply(apply(plus,x), x))).

example(4, lam(f, lam(g, lam(x, apply(f, apply(g,x)))))).

example(5, apply(fixpt, lam(fact, lam(x, 
  apply(
   apply(
    apply(if, apply(apply(equal,x),0)),
       1),
  apply(apply(mult, x),
    apply(fact, apply(apply(subt,x),1)))))))).

example(6, lam(x, apply(x, x))).

typeof(plus,  int -> int -> int).
typeof(mult,  int -> int -> int).
typeof(subt,  int -> int -> int).
typeof(if,    bool -> A -> A -> A ).
typeof(equal, A -> A -> bool).
typeof(true,  bool).
typeof(false, bool).
typeof(fixpt, (A -> A) -> A).
typeof(N,     int)    :- integer(N).
typeof(S,     string) :- string(S).


%% Next clause can be used for tracing.

% infer(G,A,B) :- print(infer(G,A,B)), nl, nl, fail.

infer(Gamma, apply(M,N), T) :- !, 
   infer(Gamma, M, (S -> T)), infer(Gamma, N, S).

infer(Gamma, lam(X,M), (S -> T)) :- !, 
  infer([(X,S)|Gamma],M, T).

infer(Gamma, X, T) :- member((X,T), Gamma), !.

infer(_, C, T) :- typeof(C, T).

