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

% 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, A).

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([tp(X,B)|O], X, A) :- unify_with_occurs_check(B, A).
synth([tp(Y,B)|O], X, A) :- Y \= X, synth(O, X, A).

