//	PFL Basic Demonstration Script
//	------------------------------
//
//	
//	This script comprises a general introduction to the features of
//	PFL.  Just read the comments (lines which start with a "//") and
//	then cut and paste the commands immediately below the comments into
//	PFL to see what happens.  Now read on.
//

//	PFL has a strong static type system.  That is, the type of every
//	expression is determined at compile time, and no run-time type
//	errors can occur.  You can find out the type of an expression, e, by
//	evaluating "_typeof e":

_typeof	1;		//	Num
_typeof 'a';		//	Char
_typeof	[1,2,3,4,5];	//	[Num]


//	New types can be introduced at any time.  For example, if we are
//	dealing with a road accident database we may want to introduce a
//	"witness" type.  This type can be populated with appropriate
//	constants:

_type	Witness		==	Type;
_value	JohnDoe		==	Witness;
_value	RichardRoe	==	Witness;


//	User defined types can be arbitrarily complex.  For example, we can
//	introduce a polymorphic "binary tree" type, and populate this with
//	the appropriate constructors:

_type	Tree		==	type0->Type;
_value	Leaf		==	Tree type0;
_value	Node		==	(Tree type0)->type0->(Tree type0)->(Tree type0);

_typeof	Node (Node Leaf 1 Leaf) 3 (Node Leaf 5 Leaf);	//	Tree Num


//	For every first-order type T the function allT returns the 0-ary
//	constructors of type T:

allWitness;


//	A selector is a function which embodies a relation.  The relation
//	expands and contracts following the inclusion of exclusion of
//	tuples.  The selector is declared and populated using the "_extsel"
//	and "_include" commands:

_extsel  |employee == (Str,Num,Str);		// (Name,Age,Boss)
_include |employee ("John",25,"Mary");
_include |employee ("Jill",25,"Mary");
_include |employee ("Jack",52,"Mary");
_include |employee ("Toby",40,"Fred");
_include |employee ("Roger",41,"Fred");
_include |employee ("Bill",42,"Fred");
_include |employee ("Mary",37,"Brian");
_include |employee ("Fred",39,"Brian");
_include |employee ("Tony",21,"Brian");
_include |employee ("Brian",19,"Angela");
_include |employee ("Mark",22,"Angela");
_include |employee ("Jane",22,"Angela");


//	A selector is a function which takes a pattern as an argument and
//	returns all tuples of the relation which match the pattern.
//	Selectors are "invertible" in that they can be queried with any
//	subset of the arguments known, and correspond to the facts or EDB of
//	a logic database:

|employee (Any,Any,"Brian");
|employee (Any,25,"Mary");


//	Tuples are removed from the relation using the "_exclude" command
//	(if you delete these facts, put them back in again since we need
//	them later in the demonstration):

_exclude |employee (Any,Any,"Angela");
_exclude |employee (Any,25,Any);


//	Programs in PFL are functions which are defined equationally.  The
//	simplest type of function takes no arguments, and may be regarded as
//	a constant:

_define	freezing_point	==	32;


//	Functions may be recursively defined (here's an old favourite!):

_define factorial 0	==	1;
_define factorial x	==	x * factorial (x-1);


//	DotDot notation is a shorthand way of denoting lists of numbers.  Try out:

[0..100];	//	[0,1,2, ..., 98,99,100]
[0..];		//	[0,1,2, ...


//	Lazy evaluation allows manipulation of infinite data structures.
//	You will need to interrupt (Control & C) the evaluation of nats 0:

_define nats n		==	n:(nats (n+1));

nats 0;


//	ZF expressions (or list abstractions) are a powerful querying mechanism:

[x | x <- [0..]; x % 2 = 0];		//	all even natural numbers

[x |	x <- [1..]; 			//	all prime numbers
	[] = [y | y <- [2..x div 2]; x % y = 0]];



//	Functions can be defined over selectors which are equivalent to the
//	rules or IDB of a logic database:

_define	hierarchy (emp,boss)	==
	[(emp,boss) |	(emp,age,boss) <- |employee (emp,Any,boss)]
++	[(emp,boss) |	(emp,age,int)  <- |employee (emp,Any,Any);
			(int,boss) <- hierarchy (int,boss)];

hierarchy ("John",Any);
hierarchy (Any,"Angela");
hierarchy (Any,Any);


//	Higher order functions allow recursion patterns to be abstracted out.  For 
//	example the function fold (given below) thas the property that
//		fold op end [v1,...vn]   =>   op v1 (op v2 ( ... (op vn end) ...) )
//	and hence it can be used to avoid explicit use of recursion in other functions
//	such as sum, product, max, min, etc (the suffix "2" is added to the
//	name of each function since they are already defined in "common.env"):

_define	fold op end []		==	end;
_define fold op end (h:t)	==	op h (fold op end t);

_define sum2 (h:t)		==	fold (+) h t;
_define prod2 (h:t)		==	fold (*) h t;
_define max2 (h:t)		==	fold greater2 h t;
_define min2 (h:t)		==	fold lesser2 h t;
_define greater2 x y		==	when (x > y) x y;
_define lesser2 x y		==	when (x < y) x y;
