jsish
Functions
Not logged in

Overview

A Jsi function may have parameters with types and/or default values eg.

#!jsish
function foo (a:number, b:string='ok'):number {
   return a+1;
}
foo('a', 9, 10);

which upon invocation outputs:

/tmp/foo.js:4: warning: got 3 args, expected 1-2, calling function foo(a:number, b:string="ok")    (at or near "a")

/tmp/foo.js:4: warning: type mismatch for argument arg 1 'a':  "string" is not a "number"    (at or near "a")

/tmp/foo.js:4: warning: type mismatch for argument arg 2 'b':  "number" is not a "string"    (at or near "a")

/tmp/foo.js:2: warning: type mismatch returned from 'foo':  "string" is not a "number"    (at or near "a")

Types are not supported for javascript variables, only function parameters/returns.

Limiting types to the function interface strikes a fine balance between flexibility and control.

Note: warnings are enabled by either of "use strict"; or "#!": See Checking.


Type Names

Supported types are:

Type Description
numberA double floating point value
booleanA boolean value
stringA string value
functionA javascript function
objectA javascript object
arrayA javascript array
regexpA regular expression
userobjA userobj command, eg. from new Socket()
nullA javascript null
undefinedA value that is undefined
voidArgument ommitted/no value returned
anyMeans any value is accepted/returned

All type names are the same as returned from typeof, except void and any.

void is used to indicate a function returns nothing:

function foo (a:number):void {
   return;
}

and any for an unspecified type:

function foo (n):any {
   return (n?99:"Ok");
}

Type Unions

Unions are multiple types separated with a pipe "|" character. eg:

function foo (a:number, b:number|string|boolean) {
    var typ = (typeof b);
    switch (typ) {
        case "number": return b+1;
        case "string": return b;
        case "boolean": return -1;
        default: throw "unhandled type: "+typ;
    }
}
foo(9,'a');
foo(9,9);
foo(9,true);

A return type may also be a union:

function foo (x):number|string {
    return x+x;
}

Builtin commands frequently use union types.


Counts

Standard javascript does not complain when the number arguments in a function call do not match the parameter list.

This is also true in Jsi, except when a function is typed:

function foo (a:number, b:number):number {
   return a+1;
}
foo(9);

The presence of a type activates checking for that function, generating warnings like:

/tmp/ss.js:4: warning: incorrect arg count in function call "foo()": got 1 args, but expected 2

Extra argument warnings can be avoided by adding an ellipsis "...":

function fool (a:number, b:number, ...) {
   return console.args.length;
}
foo(9,9,9);

It is also possible to enable argument count checking for untyped functions, by setting typeCheck mode all.


Defaults

Default values allow functions to be called with fewer parameters, as in:

function foo (a, b=1) {}
function bar (a=1, b=2) {}
foo(9);
bar();

A default value must be one of the primitives:

Type Description
numberA double floating point value
booleanA boolean value
stringA string value
nullThe null value
undefinedAn undefined value/var
voidArgument may be ommitted

Also note that when a parameter is given a default value, all following it must as well, possibly using void:

function bar (a=1, b=true, c='ok', d=null, e=void, f=void) {}

Assigning a default value also implicitly assigns a type (except void). Thus the following are equivalent:

function quick(a:number=1) {}
function quick(a=1) {}

Types are or'ed, meaning the following accepts string as well:

function duck(a:number|boolean='quack') {}

void/undefined

A default-value of void is used to indicate that an argument may be ommitted.

This still complain about calls with an undefined var argument:

#!
function bag(a=void) {}
var xyz;
bag(); //OK
bag(xyz);

Output is:

/tmp/big.jsi:5: warning: call with undefined var for argument arg 1 'a

To allow this, use a default-value of undefined instead:

#!
function fig(a=undefined) {}
function bag(a=void) {
    fig(a);
}

Note: it is illegal to use undefined in return types.


Checking

Type checking is used to issue warnings/errors for incorrect calls to a typed function.

When checking is enabled, it is applied to any function containing at least one type or default value, eg:

#!jsish
function foo (a:number, b:string='ok'):number {}

In Jsi, the default checking state is:

There are several way a script can control type-checking:

The "use" directive can take multiple comma separated options:

"use run,error";
function foo (a:number, b:string='ok'):number {}

But note again that this must be on the first line (or 2nd if using #!).

Here is the list of modes accepted by use:

Type Description
parse Turn on parse-time checking
run Turn-on run-time checking
all Both parse and run, plus argument checking for untyped functions
error Runtime warnings are errors
strict Same as all and error
funcsig Emit warnings for named function calls with no prior declaration
noundef Disable strict warnings for calls to function with void passed an undefined var
nowith Disable using with expressions
asserts Enable run-time assert() checking (See assert)
Debug Enable LogDebug() output (See logging)
Trace Enable LogTrace() output
Test Enable LogTest() output

The "!" prefix can be used to invert a flag:

"use strict,!error";

Modes may also be set from the command-line:

jsish -T parse,run foo.jsi;    # Perform checking at parse and run time.

Type-check mode can also be changed via Interp.conf():

Interp.conf({typeCheck:['error','run','parse']}); // Promote warnings to errors .

And finally, via the environment:

JSI_INTERP_OPTS='{typeCheck:["error","run","parse"]}' jsish script.jsi

Note that by default after 50 warnings, type checking is silently disabled. But this can be changed with:

Interp.conf({typeWarnMax:0}); // Unlimited warnings.

Limitations

Although parse time checking is enabled with "use strict", this is no-where close to the kind of type-checking available in C.

The first reason for this is that a javascript variable used as a function argument can not be statically type-checked, because their type is dynamic. This means only primitives can be checked.

Secondly, calls to a function can not be parse-checked prior to that functions actual definition (or forward declaration) as in:

function f(a:number):number{} // Forward declaration.

function b() {
    f(1,2);
}

function f(a:number):number {
    return a+1;
}

All builtin functions have prototypes, and therefore can be statically checked.


Miscellaneous

The rules for defining types in functions are:

  1. Zero or all parameters should be given types.
  2. Default values must be primitives.
  3. If a parameter is given a default value, all parameters following it must as well.
  4. Only functions with at least one type will be type-checked (except in mode all).

If a function has no types or defaults, it is by default treated as normal javascript, and there will be no type-checking.

Note in reference that builtin commands make extensive use of typing. In the Jsi code-base, these apply to: