%mathpiper,def="Function"

/* Defining a macro-like function that declares a function
 * with only one rule.
 */
Rulebase("Function",{oper,args,body});



// function with variable number of arguments: Function("func",{x,y, ...})body;
Rule("Function",3,2047,
	And(IsGreaterThan(Length(args), 1), IsEqual( MathNth(args, Length(args)), ToAtom("...") ))
)
[
  DestructiveDelete(args,Length(args));	// remove trailing "..."
  Retract(oper,Length(args));
  MacroRulebaseListed(oper,args);
  MacroRule(oper,Length(args),1025,True) body;	// at precedence 1025, for flexibility
];

// function with a fixed number of arguments
Rule("Function",3,2048,True)
[
  Retract(oper,Length(args));
  MacroRulebase(oper,args);
  MacroRule(oper,Length(args),1025,True) body;
];


/// shorthand function declarations
Rulebase("Function",{oper});
// function with variable number of arguments: Function() f(x,y, ...)
Rule("Function",1,2047,
	And(IsFunction(oper), IsGreaterThan(Length(oper), 1), IsEqual( MathNth(oper, Length(oper)), ToAtom("...") ))
)
[
	Local(args);
	Bind(args,Rest(FunctionToList(oper)));
	DestructiveDelete(args,Length(args));	// remove trailing "..."
	If(RulebaseDefined(Type(oper),Length(args)),
		False,	// do nothing
		MacroRulebaseListed(Type(oper),args)
	);
];


// function with a fixed number of arguments
Rule("Function",1,2048,
	And(IsFunction(oper))
)
[
	Local(args);
	Bind(args,Rest(FunctionToList(oper)));
	If(RulebaseDefined(Type(oper),Length(args)),
		False,	// do nothing
		MacroRulebase(Type(oper),args)
	);
];


HoldArgument("Function",oper);
HoldArgument("Function",args);
HoldArgument("Function",body);

%/mathpiper



%mathpiper_docs,name="Function",categories="Programmer Functions;Programming;Built In"
*CMD Function --- declare or define a function
*STD
*CALL
	Function() func(arglist)
	Function() func(arglist, ...)
	Function("op", {arglist}) body
	Function("op", {arglist, ...}) body

*PARMS

{func(args)} -- function declaration, e.g. {f(x,y)}

{"op"} -- string, name of the function

{{arglist}} -- list of atoms, formal arguments to the function

{...} -- literal ellipsis symbol "{...}" used to denote a variable number of arguments

{body} -- expression comprising the body of the function

*DESC

This command can be used to define a new function with named arguments.


The number of arguments of the new function and their names are determined by the list {arglist}. If the ellipsis "{...}" follows the last atom in {arglist}, a function with a variable number of arguments is declared (using {RulebaseListed}). Note that the ellipsis cannot be the only element of {arglist} and <i>must</i> be preceded by an atom.

A function with variable number of arguments can take more arguments than elements in {arglist}; in this case, it obtains its last argument as a list containing all extra arguments.

The short form of the {Function} call merely declares a {Rulebase} for the new function but does not define any function body. This is a convenient shorthand for {Rulebase} and {RulebaseListed}, when definitions of the function are to be supplied by rules. If the new function has been already declared with the same number of arguments (with or without variable arguments), {Function} returns false and does nothing.

The second, longer form of the {Function} call declares a function and also defines a function body. It is equivalent to a
single rule such as {op(_arg1, _arg2) <-- body}. The rule will be declared at
precedence 1025. Any previous rules associated with {"op"} (with the same
arity) will be discarded. More complicated functions (with more than one body)
can be defined by adding more rules.

*E.G. notest

This will declare a new function with two or more arguments, but define no rules for it. This is equivalent to {Rulebase ("f1", {x, y, ...})}.
In> Function() f1(x,y,...);
Result: True;
In> Function() f1(x,y);
Result: False;

This defines a function {FirstOf} which returns the
first element of a list. Equivalent definitions would be
{FirstOf(_list) <-- list[1]} or {FirstOf(list) := list[1]}.
In> Function("FirstOf", {list})  list[1];
Result: True;
In> FirstOf({a,b,c});
Result: a;

The following function will print all arguments to a string:
In> Function("PrintAll",{x, ...}) If(IsList(x),
	  PrintList(x), PipeToString()Write(x));
Result: True;
In> PrintAll(1):
Result: " 1";
In> PrintAll(1,2,3);
Result: " 1 2 3";

*SEE TemplateFunction, Rule, Rulebase,  RulebaseListed, :=, Retract
%/mathpiper_docs