%mathpiper,def="NFunction"

/* NFunction("new'func", "old'func" {arg'list}) will define a wrapper function
around  "old'func", called "new'func", which will return "old'func(arg'list)"
only when all arguments are numbers and will return unevaluated
"new'func(arg'list)" otherwise. */
LocalSymbols(NFunction'Numberize)
[
NFunction(new'name_IsString, old'name_IsString, arg'list_IsList) <-- [
	MacroRulebase(new'name, arg'list);
	MacroRule(new'name, Length(arg'list), 0,	// check whether all args are numeric
		ListToFunction({IsNumericList, arg'list})
	)

		/* this is the rule defined for the new function.
		// this expression should evaluate to the body of the rule.
		// the body looks like this:
		// NFunction'Numberize(old'name(arg'list))
		*/
			NFunction'Numberize(ListToFunction({ToAtom("@"), old'name, arg'list}));
			// cannot use bare '@' b/c get a syntax error

];

// this function is local to NFunction.
// special handling for numerical errors: return Undefined unless given a number.
10 # NFunction'Numberize(x_IsNumber) <-- x;
20 # NFunction'Numberize(x_IsAtom) <-- Undefined;
// do nothing unless given an atom

];	// LocalSymbols()

%/mathpiper



%mathpiper_docs,name="NFunction",categories="User Functions;Functional Operators"
*CMD NFunction --- make wrapper for numeric functions
*STD
*CALL
	NFunction("newname","funcname", {arglist})

*PARMS
{"newname"} -- name of new function

{"funcname"} -- name of an existing function

{arglist} -- symbolic list of arguments

*DESC
This function will define a function named "newname"
with the same arguments as an existing function named "funcname". The new function will evaluate and return the expression "funcname(arglist)" only when
all items in the argument list {arglist} are numbers, and return unevaluated otherwise.

This can be useful when plotting functions defined through other MathPiper routines that cannot return unevaluated.

If the numerical calculation does not return a number (for example,
it might return the atom {nan}, "not a number", for some arguments),
then the new function will return {Undefined}.

This operator can help the user to program in the style of functional programming languages such as Miranda or Haskell.


*E.G. notest
In> f(x) := N(Sin(x));
Result: True;
In> NFunction("f1", "f", {x});
Result: True;
In> f1(a);
Result: f1(a);
In> f1(0);
Result: 0;
Suppose we need to define a complicated function {t(x)} which cannot be evaluated unless {x} is a number:

In> t(x) := If(x<=0.5, 2*x, 2*(1-x));
Result: True;
In> t(0.2);
Result: 0.4;
In> t(x);
	In function "If" :
	bad argument number 1 (counting from 1)
	CommandLine(1) : Invalid argument
Then, we can use {NFunction()} to define a wrapper {t1(x)} around {t(x)} which will not try to evaluate {t(x)} unless {x} is a number.

In> NFunction("t1", "t", {x})
Result: True;
In> t1(x);
Result: t1(x);
In> t1(0.2);
Result: 0.4;
Now we can plot the function.

In> Plot2D(t1(x), -0.1: 1.1)
Result: True;

*SEE MacroRule
%/mathpiper_docs