% Type declarations

:- discontiguous([(:)/2]).
:- dynamic([(:)/2]).
% examples
:- dynamic([plus/3, append_/3, sort_/2, member_/2, insert/3, perm/2, diverge/0]).

% built-in types
int : [] -> type.
(+) : [int, int] -> int.

% homogeneous lists
list : [type] -> type.
[] : [] -> list(_A).
'.' : [A, list(A)] -> list(A).

% nats
nat : [] -> type.
z : [] -> nat.
s : [nat] -> nat.

% plus
plus : [nat, nat, nat] -> o.

plus(z, N, N).
plus(s(M), N, s(P)) :-
	plus(M, N, P).

% for testing purposes
sort_ : [list(int), list(int)] -> o.
sort_([2,1], [1,2]).

% member_(X, Ys) iff X is a member of the list Ys
% will succeed multiple times for each member of Ys equal to X.
member_ : [A, list(A)] -> o.
member_(X, [X|_]).
member_(X, [_|Ys]) :- member_(X, Ys).

% append_(Xs, Ys, Zs) iff Zs is the result of appending list Ys to list Xs
append_ : [list(A), list(A), list(A)] -> o.
append_([], Ys, Ys).
append_([X|Xs], Ys, [X|Zs]) :- append_(Xs, Ys, Zs). 

% insert(X, Ys, Zs) iff Zs is result of insert X somewhere in Ys
insert : [A, list(A), list(A)] -> o.
insert(X, Ys, [X|Ys]).
insert(X, [Y|Ys], [Y|Zs]) :- insert(X, Ys, Zs). 

% perm(Xs, Zs) iff Zs is a permutation of Xs
perm : [list(A), list(A)] -> o.
perm([], []).
perm([X|Xs], Zs) :-
	perm(Xs, Ys),
	insert(X, Ys, Zs).

% diverge will never terminate
diverge : [] -> o.
diverge :- diverge ; true.
