-
Notifications
You must be signed in to change notification settings - Fork 5
Functions LRM
All functions in JSJS are Lambda expressions. Since functions are treated as first class citizens, these lambda expressions can be assigned as values to identifiers, passed as arguments to other functions, and returned as values from other functions.
To make a named function declaration, a lambda expression is assigned to an identifier using the val
keyword, just like any other type declaration.
Lambda expressions are denoted by the symbol /\
, which resembles the upper case Greek letter Lambda.
JSJS Lambda expressions have the following form:
/\(argument declarations if any) : return type => {
Block of expressions, the last of which
is the value that is returned
}
A shorthand syntax is also supported if the body of the Lambda is a single statement:
/\(argument declarations, if any) : return type => expression
The argument declarations must be annotated with their types, and a return type of the body of the /\ expression also must be specified.
For example, the following /\ expression takes a single number x
as an argument and evaluates the square of x
.
/\(x : num) : num => x * x
It is also possible to define Lambda expressions that don't take any arguments:
/\() : unit => println("नमस्ते")
or those that take multiple arguments:
/\(fname : string, lname : string) : string => "Hello " ^ " " ^ fname ^ " " ^ lname
The body of a Lambda can also be a block of expressions, where the last expression is the one that is implicitly evaluated and returned. The following /\ takes a single numeric argument x
and adds the value y
- assigned as 10 in the body - to x
.
/\(x : num) : num => {
val y = 10;
x + y;
}
AST for /\ expressions:
type expr:
| FunLit of func_decl
func_decl = {
formals : (string * primitiveType) list;
return_type : primitiveType;
body : expr;
}
Grammar:
literals:
| LAMBDA LPAREN formals_opt RPAREN COLON primitive FATARROW expr %prec ANON {
FunLit({
formals = $3; return_type = $6; body = Block([$8]);
})
}
| LAMBDA LPAREN formals_opt RPAREN COLON primitive FATARROW block {
FunLit({
formals = $3; return_type = $6; body = Block($8);
})
}
When an identifier is assigned to a /\ expression, it becomes a value of the function type. The type of a function is
fn : (A,B) -> C
Here, fn
takes 2 arguments of type A
and B
respectively, and evaluates an expression or block of type C
.
In general, a function type is a list of input argument types (optional) and return type.
When assigning an identifier to a /\ expression using the val
keyword, annotating the identifier with the function type is optional, just like type specifiers for all other expressions.
// function type explicitly specified
val cube : (num) -> num = /\(x : num) : num => x * x * x ;
// function type of val not annotated
val whatDoYouKnow = /\(name : string) : string => {
if name == "John Snow"
then "Nothing"
else "Something" ;
} ;
AST:
type primitiveType =
| TFun of funcType
and funcType = primitiveType list * primitiveType
Grammar:
primitive:
| LPAREN args RPAREN THINARROW primitive { TFun($2, $5) }
args:
| args = separated_list(COMMA, primitive) { args }
Functions are called by invoking the name of the function and passing it actual arguments. These arguments can be any kind of expressions.
Every function call is itself an expression, and evaluates to a value which has a type (the return type of function).
// function called with a numeric literal
val sq5 = sq(5)
// function called with an expression
val x = 8;
val y : num = sq(x);
val z = cube(x + y);
// function called with a function as an argument
val addOne = /\(x : num) : num => x + 1;
val addOneSqr = /\(f: (num) -> num, g: (num) -> num, x : num) : num => f(g(x));
val result = addOneSqr(sq, addOne, 8);
AST:
type expr =
| Call of string * expr list
Grammar:
expr:
| ID LPAREN actuals_opt RPAREN { Call($1, $3) }
JSJS © 2016