|
|||||
|
|||||
Using Jaskell
Using The Jaskell Programming LanguageJaskell 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.
As many other programming languages, Jaskell supports number literals, character literals and string literals. 1, 3.5, "abc", 'x' are all valid literals. A list is a sequential data structure.
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. <<<|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. <<<<hello>>>>
<<<(hello)>>>
<<<{hello}>>>
$$<<hello>>$$
$$<(hello)>$$
$$<{hello}>$$
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. "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". 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 is the only native conditional statement in Jaskell. Like many imperative languages such as Java, the "else" clause is optional. When 'else' clause is omitted, 'null' is used as the value of this clause. The following expression evaluates to 5: if 1==1 then 1 else 5 There's no native switch-case support in the Jaskell programming language. However, the 'jaskell.prelude.switch' function can be used for this purpose. switch (3-2) .case(0, "zero") .case(1, "one") .default("unknown") .end evaluates to "one". Similar to Java, Jaskell supports the following binary operators with natural semantics: 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. 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 '=>' operator is a variant of "if-then'. "cond => x" is equivalent to "if cond then x". '=>' is typically overridden to provide "deduction" kind of logic. ':=' is an operator with no predefined meaning. It can be overloaded to provide custom semantics though. '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. 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. 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. The same name can be re-defined though. 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' 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. a+b where a = b+1; b = 2; end evaluates to 5. 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. const v _ = v;
It is possible to omit function name. Such function is called "lamda". Symbol ' add where add a b = a+b end is equivalent to \a b -> a+b When defining function, different function body can be provided based on the pattern of certain parameters. Symbol '|' is used to seperate different patterns. 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. test t@{ind} = t
| _ = {};
Similar to function definition, there're two alternative syntax for calling a function. add a b = a+b; add 1 2 is equivalent to add(1,2) A new syntax added since version 0.9 is the "->" operator. "f a" and "f(a)" are equivalent to a->f This new syntax helps when function calls are chained, because "a->h->g->f" is obviously easier to read than "f(g(h(a)))". 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 '$' reads as "apply". 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. Such parenthesis may become ugly in more complex expressions. Operator '->' reads as "arrow". '->' operator is used to provide an alternative syntax for calling function, wherever it looks fit. Operator '=>' reads as "then". '=>' operator is typically overriden to provide custom "deduce" kind of logic. Operator ':=' is an operator with no predefined meaning. It needs to be overriden either globally or in a tuple to be used. It is typically to implement 'assignment' kind of logic. Operator '<<' reads as "compose". Operator '>>' is used to enforce evaluation order. expr = expr1 >> expr2; This is useful when expr1 has side-effects. Jaskell functions are call-by-need. const 5 invalid where invalid = invalid; const v _ = v; end will evaluate to 5 because the "invalid" parameter is never evaluated. 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:
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. In order to get the unary "negative" operator, use (~) instead. Jaskell allows using function as an infix binary operator. 1 `add 2 where add a b = a+b end evaluates to 3. 1 `(add3 2) 3 where add3 a b c = a+b+c; end evaluates to 6. 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}. 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"}. As we mentioned earlier, tuple member names are not visible as function names to the member bodies. {firstname="Tom"; lastname="Swan"; fullname="$firstname $lastname"}
because "firstname" and "lastname" are not visible to the definition of "fullname". {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". 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. {firstname="Tom"} `extends {lastname="Swan"}
{firstname="Tom"} `includes {lastname="Swan"}
The difference is how 'this' is bound: 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
Any Java class can be imported using the 'jaskell.java.import' function. jaskell.java.import "java.lang.StringBuffer" Each Java class supports a special member named "new" to create an object of this class by calling one of its constructors. 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[] In order to get the class of an array type, the 'jaskell.java.array' function can be used. 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. For any Java object, its public field can be referenced in Jaskell. 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. person.setName["Jack"]
or Person.new["Tom", 10] 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. 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 |
|||||
|
Copyright 2003-2006 - The Codehaus. All rights reserved unless otherwise noted.
Powered by Atlassian Confluence
|
|||||