Using The Jaskell Programming Language

Jaskell is a lazy functional scripting language for Java. It features higher-order function, function currying, string interpolation, lazy evaluation, monad, dynamic typing, simple syntax and semantics etc.

Literals

As many other programming languages, Jaskell supports number literals, character literals and string literals. 1, 3.5, "abc", 'x' are all valid literals.


list

A list is a sequential data structure.


here-docs string literal

Except for string literal enclosed by double quote, Jaskell supports here-docs syntax as well. i.e. almost any character can appear in the string. Two different syntax are supported for here-docs:

Any non-alpha character can be picked as the leading and terminating character for here-docs string literal.
The leading character is the character following "<<<" or "$$<".
The terminating character is the character followed by ">>>" or ">$$".
The following expressions all evaluate to "hello":

<<<|hello|>>>
<<<!hello!>>>
<<<-hello->>>
<<<^hello^>>>
$$<|hello|>$$
$$<!hello!>$$
$$<-hello->$$
$$<^hello^>$$
Special rules apply for '<', '>', '[', ']', '{', '}', '(', ')'. These characters have a natural matching character, so if you pick any one of them as the leading character, the matching character needs to be the terminating character.
The following expressions all evaluate to "hello":
<<<<hello>>>>
<<<(hello)>>>
<<<{hello}>>>
$$<<hello>>$$
$$<(hello)>$$
$$<{hello}>$$


string interpolation

String literal denoted by double quote and "$$<<...>>$$" style supports string interpolation.

In such string literal, a word prefixed by a '$' is interpreted as a variable name. The string representation of this variable will be evaluated and embedded in the string at runtime.

Also, an expression enclosed by '${' and '}' is evaluated and the string representation of the value will be embedded in the string at runtime.
For example:

"hello $name" where
  name="Tom"
end
evaluates to "hello Tom".
$$<<I said: ${greeting "Tom"}>>$$ where
  greeting name = "hello $name";
end
evaluates to "I said: hello Tom".


tuple

A tuple is an associative array.

',' or ';' can be used to seperate tuple members.

Tuple member can be de-referenced with a '.'. For example:

{name="tom"; age=1}.name
evaluates to "tom".


if-then-else

if-then-else is the only native conditional statement in Jaskell.

Unlike many imperative languages such as Java, the "else" clause is mandatory.

The following expression evaluates to 5:

if 1==1 then 1 else 5


switch-case

There's no native switch-case support in the Jaskell programming language.

However, the 'jaskell.prelude.switch' function can be used for this purpose.
For example:

switch (3-2)
.case(0, "zero")
.case(1, "one")
.default("unknown")
.end
evaluates to "one".


Operators

Similar to Java, Jaskell supports the following binary operators with natural semantics:
'==', '!=', '>', '<', '>=', '<=', '+', '-', '*', '/', '%', '&&', '||', 'and', 'or'.

Slightly different from Java though, '==' and '!=' calls "Object.equals()", and comparison operators such as '>', '<' calls "Comparable.compareTo()".

Hence, the following expressions all evaluate to true:

"abc"=="abc"
"abc"!="ABC"
"10"<"2"
"abc"=="abc" and "abc"!="ABC"
"abc"!="ABC" or "10"=="2"
'and' and '&&', 'or' and '||' are equivalent.

Unary operators such as '!', '~', 'not' are also supported. 'not' and '!' are equivalent. They are both the logical negation operator. '~' reads "negative". "~1" is same as "-1".

Jaskell supports ':', '++', '@', '#' operators for list operation.
':' is used to prepend an element to a list.

1:[2,3]
evaluates to [1,2,3]

'++' is used to concatenate two lists together:

[1,2]++[3,4]
evaluates to [1,2,3,4]

'@' is used to get an element from a list/tuple by index/key:

[1,2,3,4]@1
evaluates to 2.
{name="tom";age=10}@"age"
evaluates to 10.

Besides (@), jaskell also supports the familiar "[]" notion as a syntax sugar to indicate a list/array subscript. For example,

[1,2,3,4][1]
also evaluates to 2, which is exactly the same as the (@) operator.

Another use of the "[]" syntax sugar is to indicate an array type, so one can say:

int[]

'#' is used to get the size of a list/tuple:

#[1,2]
evaluates to 2.

For array and list, the "length" method can also be called to read the length, which is more familiar to most Java programmers:

[1,2].length


let

'let' is a keyword that allows definition of variable and function.

let
  a = 1;
  b = 2;
  a+b;
end
is an expression that evaluates to 3.

'let' clause is an expression that's terminated by an 'end' keyword.

Enclosed by 'let' and 'end' are a list of statements.
Each statement can be either an expression or a definition.

When the "let-end" expression is evaluated, the statements are evaluated sequentially.

The value of the last statement is the value of the "let-end" expression.
Statements are seperated by a ';'.

Recursion is not allowed in "let-end".

let
  fact n = if n==0 then 1 else n*fact(n-1);
end
will report an error because the recursive use of name "fact" is not recognized.
In fact, only names defined prior to the current statement are visible.

The same name can be re-defined though.
For example:

let
  a = 1;
  b = a+1;
  a = 2; //line 3
  c = a+b;
end
At line 3, the name "a" is redefined so that the value of c becomes "2+b". And since line 3 is a "re-definition", not "assignment", the value of b is not changed. Therefore, the above expression evaluates to 4.


where

'where' is another keyword to define variables and functions.

Enclosed by 'where' and 'end' are a list of definitions.

Recursion is allowed in the definitions. In fact, all names defined in "where-end" clause are visible to each other.

Unlike "let-end", re-definition of the same name is not allowed.

The order of the definitions does not matter.
For example:

a+b where
  a = b+1;
  b = 2;
end
evaluates to 5.


function

The following code defines a simple function that adds two values together:

add a b = a+b;
Function parameters can be listed directly after the function name and seperated by whitespace.

An alternative syntax is more like Java:

add(a,b) = a+b;
where parameters are enclosed by a pair of parenthesis and seperated by ','.

These two syntax are totally equivalent though.

When a parameter is not used in the function body, its name may be omitted.
In this case, the special symbol '_' can be used to indicate an anonymous placeholder. For example:

const v _ = v;


lamda function

It is possible to omit function name. Such function is called "lamda". Symbol '\' is used to denote a lamda function.

add where add a b = a+b end
is equivalent to
\a b -> a+b


pattern match

When defining function, different function body can be provided based on the pattern of certain parameters.

Symbol '|' is used to seperate different patterns.
For example, the following function reverse a list:

reverse [] = [] //in case of empty list
|       x:xl = reverse xl ++ [x]; // in case of non-empty list.

The following function tests if a parameter is a tuple with a member named "ind":

hasMember {ind} = true
|         _     = false;

It is possible to name a pattern, so that this name can be used to reference the object of this pattern.
Special symbol '@' can be used to name a pattern.
The following function returns the tuple itself if it contains member "ind", an empty tuple is returned otherwise:

test t@{ind} = t
|    _       = {};


Calling function

Similar to function definition, there're two alternative syntax for calling a function.
For the following "add" function:

add a b = a+b;
add 1 2
is equivalent to
add(1,2)


function currying

Jaskell function can be curried. i.e. Functions can be partially applied. For example:

let 
  add a b = a+b;
  inc = add 1;
  inc 2;
end

Function "inc" is obtained by giving function "add" only one parameter.

The result is a closure that holds this parameter value and waits for the arrival of the missing parameter.

"inc 2" provides the missing parameter and finally apply the function "add".


($) operator

Operator '$' reads as "apply".
f $ 1 is equivalent to f 1 where the right operand is passed to the left operand as function parameter.

The '$' operator looks useless at the first glance because literally adding a '$' in between the function and the parameter makes no difference at all.

The value of '$' operator is to change the precedence.

f1 f2 x is equivalent to (f1 f2) x.
But if we need to pass x to function f2 first, the code has to be changed to f1 (f2 x), which requires a parenthesis to force the precedence.

Such parenthesis may become ugly in more complex expressions.
In such case, '$' can help.
'$' is an operator with the lowest precedence, f1 $ f2 x is equivalent to f1(f2 x).


(<<) operator

Operator '<<' reads as "compose".
f1 << f2 creates a function whose first parameter is passed to f2, and the return value of f2 is passed to f1. Thus f1 $ f2 x is equivalent to (f1<<f2) x.


(>>) operator

Operator '>>' is used to enforce evaluation order.
When the following definition is evaluated, expr1 is first evaluated, then expr2 is evaluated.

expr = expr1 >> expr2;
This is useful when expr1 has side-effects.


call-by-need

Jaskell functions are call-by-need.
A parameter is not evaluated until needed. For example:

const 5 invalid where
  invalid = invalid;
  const v _ = v;
end
will evaluate to 5 because the "invalid" parameter is never evaluated.


where vs. let vs. {}

functions and variables can be defined in three different contexts: where clause, let-end expression and a tuple.

Although the syntax looks very similar, different rules apply.

In 'where' clause:

In "let-end" expression:

In a tuple declaration:


operator as function

Any operator can be used as a function by enclosing it in a pair of parenthesis.

For example: (+) 1 2 is equivalent to 1+2.
Although '-' can also be used as the unary negative operator, (-) is always treated as the binary "minus" operator.

In order to get the unary "negative" operator, use (~) instead.


function as operator

Jaskell allows using function as an infix binary operator.
The backward apostrophe '`' can be used to denote an infix function.
For example:

1 `add 2 where
  add a b = a+b
end
evaluates to 3.
When the function used as infix operator is a more complex expression, parenthesis is needed to enclose the expression, for example:
1 `(add3 2) 3 where
  add3 a b c = a+b+c;
end
evaluates to 6.


slicing tuple

A tuple can be "sliced" to create a new tuple that is a subset of the source tuple.

{name="tom";age=10;sex="male"}.{name,age}
evaluates to {name="tom";age=10}.
Note, slicing a tuple does not evaluate any tuple member.


extending tuple

A tuple can be "extended" to create a new tuple with new members included.

{name="tom";age=10}.{sex="male"}
evaluates to {name="tom";age=10;sex="male"}.
Note, extending a tuple does not evaluate any tuple member.


'this' variable

As we mentioned earlier, tuple member names are not visible as function names to the member bodies.
The following expression reports error:

{firstname="Tom"; lastname="Swan"; fullname="$firstname $lastname"}
because "firstname" and "lastname" are not visible to the definition of "fullname".
In order to reference these two members of the current tuple, the special variable 'this' has to be used:
{firstname="Tom"; lastname="Swan"; fullname="${this.firstname} ${this.lastname}"}
The member "fullname", when evaluated, will evaluates to "Tom Swan".

Since 'this' always binds to the current tuple, it is essentially a late-binding mechanism. For example:

{firstname="Tom"; lastname="Swan"; fullname="${this.firstname} ${this.lastname}"}
.{firstname="Jack"}.fullname
will evaluate to "Jack Swan" because 'this' now points to a tuple whose firstname is "Jack".


'extends' and 'includes'

The "tuple.{firstname=...}" syntax can be used to statically extend a tuple.

It is also possible to dynamically merge the members of two tuples together.

'jaskell.tuple.extends' and 'jaskell.tuple.includes' are two functions for this purpose.
For example, the following two expressions both evaluate to {firstname="Tom"; lastname="Swan";} :

{firstname="Tom"} `extends {lastname="Swan"}
{firstname="Tom"} `includes {lastname="Swan"}

The difference is how 'this' is bound:
The 'extends' function binds 'this' to the new tuple for all members;
While 'includes' binds 'this' for each member to the original tuple that owns the member.

So the following expression using 'extends' is legal:

({firstname="Tom"} `extends {lastname="Swan", fullname="${this.firstname} ${this.lastname}}})
.fullname
while the following expressiong using 'includes' is not:
({firstname="Tom"} `includes {lastname="Swan", fullname="${this.firstname} ${this.lastname}}})
.fullname


import Java class

Any Java class can be imported using the 'jaskell.java.import' function.
For example:

jaskell.java.import "java.lang.StringBuffer"


new

Each Java class supports a special member named "new" to create an object of this class by calling one of its constructors.
For example:

StringBuffer.new[]

For convenience, a global "new" function is also provided to mimic the Java "new" operator. Therefore the above code can be written as:

new StringBuffer[]


array

In order to get the class of an array type, the 'jaskell.java.array' function can be used.
For example:

intarray.new 5 where
  intarray = jaskell.java.array int;
end
will create an int[] object with size 5.

The "[]" syntax sugar can also be used so that the above example can be re-written as:

int[].new 5

Note, Jaskell doesn support the "new int[5]" syntax currently.


object

For any Java object, its public field can be referenced in Jaskell.
For example, for the following class in Java:

class Person{
  public String name;
  public int age;
  ...
  public void setName(String n){...}
  public Person(String n, int a){...}
}
A Person object's field can be referenced in Jaskell as:
person.name

To invoke methods or constructors, the parameters need to be put into a list.
For example:

person.setName["Jack"]
or
Person.new["Tom", 10]


java bean

Java bean properties can be read either by calling the getter method, or, more conveniently, by referencing the property name directly as if it were a field.
For example, for the following class in Java:

class Person{
  private String nm;
  private int age;
  ...
  public String getName(String n){return nm;}
  public Person(String n, int a){...}
}
In order to get the value of the "name" property of a person object, the code can simply say:
person.name