:- module(_,_,[]).

%% Functions can be coded as predicates with one more 
%% argument, which represents the output.

% E.g., the Ackermann function:
% 
% ackermann(0,N) = N+1
% ackermann(M ,0) = ackermann (M-1 ,1)
% ackermann(M,N) = ackermann (M-1, acker0mann (M,N-1))
% 
% is represented as plain clauses as follows:
% 
% ackermann(0,N,s(N)).
% ackermann(s(M),0,Val) :-  
%     ackermann(M,s(0),Val).
% ackermann(s(M),s(N),Val) :- 
%     ackermann(s(M),N,Val1),
%    ackermann(M,Val1,Val).
% 
% The fsyntax and functional packages allow writing 
% the clauses above with functional syntactic sugar: 

:- use_package(functional).
:- op(100,fy,s). 
% (defines s as a prefix operator to save us 
%  writing parenthesis)

ackermann(  0,   N) := s N.
ackermann(s M,   0) := ackermann(M, s 0).
ackermann(s M, s N) := ackermann(M, ackermann(s M, N) ).

% Try: 
% ?- op(100,fy,s).
% (defines s as a prefix operator to save us 
%  writing parenthesis)
% ?- ackermann(s s 0, s s s 0, X).

% But now it also runs in other modes:
% ?- ackermann(s s 0, s s s 0, s s s s s s s s s 0).
% ?- ackermann(s s 0, Y, s s s s s s s s s 0).
% ?- ackermann(X, s s s 0, s s s s s s s s s 0).

% Convenient in other cases – e.g. á for defining types:
nat := 0 | s nat.
% Try: 
% ?- nat(s s 0).
% ?- nat(a).
% ?- nat(X).
