jsish
Functions
Not logged in

Overview

A Jsi function differs from ecma-script in that parameters may have types and default values.

Here is an example of a typed function:

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

upon execution this 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")

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

Note: types are supported for function parameters only, not general javascript variables.


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.

However, this will 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);
}

Checking

Checking is applied to functions containing at least one type or default value. There are a number of ways to enable type-checking.

The easiest way just adds "use strict"; to the beginning of a script file.

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

The "use" directive (which must be on the first line, or 2nd if using #!), takes multiple comma separated options:

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

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

"use strict,!error";

Here is the list of modes:

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 Promote runtime warnings to errors
strict Same as all and error
proto Emit warnings for named function calls with no prior declaration
noundef Disable warnings for calls to function with void passed an undefined var
nowith Disable the strict-mode warning if using with

Interactive mode enables strict by default. Moreover, when a #! is used on the first line, runtime warnings are turned on.

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 they have no type. This means only primitives can be checked.

Secondly, calls to a function can not be parse-checked prior to that functions actual definition, or a 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 functions are:

  1. All or no parameters should be given types.
  2. A default value can only be a primitive.
  3. When a parameter is given a default value, all following it do as well.
  4. Functions with a type are type-checked.

If a function has no types or defaults, it is by default treated in normal javascript fashion. ie. no type-checking is applied.

Although not discussed here, a peek at the reference shows that builtin commands make extensive use of typing.

In the Jsi code-base, these extend to: