% propositions A,B ::= and(A,B) | imp(A,B)
% checkable terms M,N ::= pair(M,N) | fun(X,M)
% synth terms R ::= fst R | snd R | ...
% context O ::= (x,A) . O | nil

mem(tp(X,A), [tp(Y,B)|Xs]) :- X = Y, unify_with_occurs_check(A,B).
mem(tp(X,A), [tp(Y,B)|Xs]) :- X \= Y, mem(tp(X,A), Xs).

% check(+O, +M, +A), check(+O, +M, ?A)
% synth(+O, +R, -A), check(+O, +M, ?A)

check(O, pair(M,N), and(A,B)) :-
    check(O, M, A),
    check(O, N, B).
check(O, fun(X,M), imp(A,B)) :-
    check([tp(X,A)|O], M, B).
check(O, R, A) :- synth(O, R, B), unify_with_occurs_check(A,B).

synth(O, fst(M), A) :- synth(O, M, and(A,B)).
synth(O, snd(M), B) :- synth(O, M, and(A,B)).
synth(O, app(R,M), B) :-
    synth(O, R, imp(A,B)),
    check(O, M, A).
synth(O, X, A) :- mem(tp(X,A), O).
