Type checking in FrAid
FrAid is weakly typed
FrAid is weakly typed - there is no restriction what values could be assigned to variables or returned by functions and everything evalues to a Complex but just like other weakly typed languages the functions could place restrictions on what their arguments are.
Examples:
//function which takes numbers and strings and returns the oposite type f(x) = if isString(x) then 5 else if isComplex( x ) then "hello" else "don't know"; //a function which does not check the types (uses the fact that everything evaluates to a number) subtract( "hello", 3); //will return -3 because the string evaluates to 0 //a function which places restrictions on its arguments printTree(2); // returns error - Argument not a DefinedFunction
Checked types
In FrAid you can use these functions to check what is behind a returned/passed value (by a Function or a Variable):
isComplex() isFunction() isString() typeOf()
In your Java code for the same purpose you you can use:
org.fraid.complex.ComplexHelper.ensureString( Complex ); org.fraid.complex.ComplexHelper.ensureComplex( Complex ); org.fraid.complex.ComplexHelper.ensureComplexFunction( Complex ); org.fraid.complex.ComplexHelper.ensureDefinedFunction( Complex );
The following is-a hierarchy shows how the FrAid "types" relate to each other (the same names are used in the rest of the documentation). The asterisk denotes checked type. If a type is checked a value of a parent's type could be passed as an argument but only those of the appropriate type will be accepted:
- Complex - everything evaluates to a Complex, if not Complex* would generally evaluate to 0+0i;
- Complex* - checked Complex, accepts only complex numbers (3+4i) or a Function which evaluates to a complex number;
- Boolean - a checked Complex could be a Boolean;
- String - something is considered a String if SimpleNode.m_text != null;
- String* - checked String, accepts only strings ("hello") or a Function which evaluates to a string;
- Function* - a single quote character followed by a function signature ('sin(x)), the names of the arguments are insignifficant, the number is important;
- Variable* - a function with no arguments (Pi(); or Pi; ), the brackets can be ommited;
- UserDefinedVariable* - a no arguments UserDefinedFunction;
- UserDefinedFunction* - a function defined by the user (vs. the Java defined functions) - f(x,y)=x*sin(y)+1;
- UserDefinedVariable* - a no arguments UserDefinedFunction (a=5;) ;
- Variable* - a function with no arguments (Pi(); or Pi; ), the brackets can be ommited;
- Boolean - a Complex could be interpreted as a true/false value. 0+0i is false, everything else is true.
- Complex* - checked Complex, accepts only complex numbers (3+4i) or a Function which evaluates to a complex number;
Function types
A function is uniquely identified by its name and the number of its arguments. For FrAid the type of the arguments
is insignifficant and if there are any restrictions placed upon them by the function itself FrAid remains unaware.
Example:
//although these functions both take only a particular type of argument //FrAid won't see any difference and will just override the first one //(the Java defined functions can not be overriden) f(x)= if isString(x) then "OK" else "ERROR"; f(x)= if isComplex(x) then "OK" else "ERROR"; //this is OK, different name g(x)= if isComplex(x) then "OK" else "ERROR"; //or this, different number of arguments f(x,y)= if isComplex(x) then "OK" else "ERROR";
Defined Functions
The user defined FrAid functions can be overriden at any moment of the FrAid execution.
Example:
f(x)=x^2; plot('f(x)); f(x)=sin(x); //see what happens to the plot when you change the definition of f(x)
The user defined FrAid functions can only have fixed number of arguments.
The user defined FrAid functions can only return Complex, String and Boolean (see Checked Types).
Library Functions
The Library Functions can not be overriden by user defined functions
sin(x)=x*x; //will return Trying to override Java defined function error
The Library Functions can take:
- Fixed number of arguments - add(x,y)
- Variable number of arguments - sum(x,y,z) vs. sum(x).
The Library functions can return Complex, String and Boolean (see Checked Types). Although not enforsed in the grammar the preffered way for a library function to return/define a function is to take a String argument for the function name and register the function (still returning Complex, String or Boolean). The rest of the code then can use the newly registered function addressing it by name. For examples see rk(), rk1() and fourier().
Library Functions with variable number of arguments
A Library Function with variable number of arguments can coexist with any other function witht the same name but fixed number of arguments.
A Library Function with variable number of args. can place restriction on the number of its arguments. sampledF() for instance won't work unless an odd, greater than seven number of arguments is passed (the first of which is a String, see type checks and examples).
If two functions with the same name exist, one with fixed number the other with variable number of args. and a call is made for a function with this name and the number of args of the fixed number function, the fixed number function takes precedence.
Sampled Functions
A Sampled Function is the equivalent of a Vector of a certain length in other languages with the difference that in FrAid they have a start point attached to the first element and a (fixed!) step between its elements:
v(x)=samples(1,1,2,2,2); //create a sampled function which starts at 1, //has a step=1 and three elements equal to 2; v(x)=vector(2,2,2); //equivalent to the one above
This way the sampled functions can be used anywhere non-sampled functions can be used:
plot({v+sin}); //add a sampled and non-sampled function
Once created the start point and step of a Sampled Function can be checked (from the example above):
startS(v); // --> 1 stepS(v); // --> 1
or changed:
startS(v,.5); stepS(v,.3); startS(v); // --> .5 stepS(v); // --> .3
The length of a Sampled Function could be checked with lengthS(v); // --> 3 but can not be changed without redefining the whole function.
The individual elements can be checked or changed using the elemS() function.
Sampled Functions can be concatenated, truncated, padded, shift-rotated, etc.
As you may have noticed many functions which deal with Sampled Functions have the 'S' postfix in their names (printS, stepS, startS, ...)
Every non-sampled function could be sampled:
f(x)=sin(x)+cos(x); //non-sampled function fs(x)=sampleL(f, 0, 1, 1000); //fs(x) is the sampled equivalent of f(x) in the interval [0,1]
The main purpose of the Sampled Functions is to allow DSP and processing of sampled data (like signals from the sound card). For consistency with the non-sampled functions outside of the interval where the samples are defined the function evaluates to zero.
Generator Functions
Generator Functions can be used in two contexts:
- Function definitions: fs(x)=sampleL(f, 0, 1, 1000);
- Where a function is expected as an argument: plot(sampleL(f, 0, 2*Pi, 1000)); in which case a hidden function is registered (in this respect they are very similar to the meta expressions)
The Generator functions can create both sampled or non-sampled functions. For example sampleL(), truncateS create Sampled Functions while firResp(), icft() create regular "analog" functions.
Functions generated by Meta Expressions
The FrAid meta expressions ({xxx}) generate hidden functions which are passed where a function is expected as an argument: plot({sin+cos});. As already noted, the meta expressions are just a short hand and everything in FrAid could be done without their use.