Control Flow (If-then-else)

The only control flow the language has is “if-then-else”.

The use of “if-then-else” is very intuitive.

A few examples will say it all.

 

Ø       if 10>2 then “yes” else “no”

yes

Ø       if #”hello” == 5 then 5 else “bad”

5

Ø       if “hello”@0=’e’ then 0

Ø       else if “hello”@1 == ‘e’ then 1

Ø       else “not found”

1

Unlike C/C++/Java, if-then-else is an expression. It can be used in any place where an expression is expected. For example:

Ø       (if 1>2 then 1 else 2)+3

5

Since if-then-else is an expression, the “if-then” statement in C/Java is not permitted. This is because you need a value for the “else” anyway.

 

Function

When I said “if-then-else is the only control flow in Jaskell”, you may start wondering “what kind of language is that?”

No, you didn’t hear me wrong. Jaskell has no “for loop”, no “switch-case”, no “throw-catch” in the langauge.

But, as the ancient Entish motto says: “Don’t be hasty”.

These things, however nice to have, can be achieved by using functions.

 

And now we’ve arrived the most exciting part of Jaskell: function.

 

Function definition syntax

A simple function definition is composed of a function name, a list of parameter names and an expression as the body.

<fundef> ::= <fun name> (param_name)* ‘=’ <expr>

For example:

Ø       add a b = a+b

Unlike C/Java, parameters are neither enclosed by parenthesis, nor seperated by comma.

The expression on the right side of the ‘=’ is the function body.

 

A function can be parameterless. For example:

Ø       say_hello = println “hello”

If a parameter is not used in the function body, it does not have to be named. the reserved ‘_’ character can be used as a placeholder for such parameters.

For example:

Ø       const c _ = c

The const function will return the first parameter as return value and ignore the second. In this case, the second parameter can remain unnamed and use ‘_’ as the placeholder.

 

This is pretty much the function definition syntax. When we introduce pattern match, you will see more complex syntax for function definition.

 

Function call sytnax

Function call syntax is very similar to function definition. Arguments are listed directly after the function name. Unlike C/Java, neither parenthesis nor comma is required.

<function_call> ::= <expr> (<expr>)*

For example, to call the add function defined previously:

Ø       add 1 2

3

Thanks to my friend, Eliminster. He made me realize that people with Java background may still prefer to use “f(a,b,c)” syntax rather than “f a b c”.

So, Jaskell also supports the Javaish function call syntax.

To call add function, you can also say:

Ø       add(1,2)

Both syntax yield the same result. There’s no semantic difference between these two syntaxes. In fact, you can even mix them. For example:

Ø       add 1 (2,3)

6

Ø       add(1,2) 3

6

Function call precedence

Function call in jaskell has the highest precedence. So,

Ø       add 1 2 + 3

is equivalent to

Ø       (add 1 2)+3

or

Ø       add(1,2)+3

 

Parenthesis can be used to group expression for cases where the default precedence is not what we want.

Parenthesis can be used to group expressions.

Ø       add 1 (add 2 3)

6

Ø       add (1) (add 2 3)

6

 

Also, the Javaish syntax can also be used to achieve the same goal.

Ø       add(1, add 2 3)

6

 

The ‘$’ operator

Except for using parenthesis and Javaish syntax, there’s another way to change precedence. The nice thing about this is, it is free of parenthesis and hence can avoid code cluttering in some cases.

 

The operator ‘$’ can be used to separate function and its argument.

f $ a has the same semantics as f a. Except it changes the precedence.

 

Ø       f a b c

is equivalent to

Ø       f(a,b,c)

 

While

Ø       f $ a b c

is equivalent to

Ø       f(a(b,c))

 

‘$’ operator is useful when the argument is a long expression. In that case, it can save you from enclosing such long expression with parenthesis.

It is also desirable when what we want is the following structure:

Ø       f1(a, f2(b, f3(c, f4(d, e))))

In this case, the parenthesises are very ugly.

Using ‘$’, the above expression can be written as:

Ø       f1 a $ f2 b $ f3 c $ f4 d e

This is because ‘$’ is right-associative and has the lowest precedence.

Where clause

 

Jaskell functions are defined with keyword “where”.

 

where” is used to define functions and variables used in an expression.

 

For example:

 

Ø       add 1 2 where

Ø         add i j = i+j

3

More than 1 functions and variables can be defined in one “where” clause.

Ø       add 1 2 + sub 1 2 where

Ø         add a b = a+b;

Ø         sub a b = a-b;

2

“;” is used to separate function definitions declared in the same “where” clause.

Unlike haskell, indentation is not important in Jaskell. Of course it is always desirable to align the code beautifully.

 

Scope

Definitions in a where clause is only visible in that where clause and the expression followed by that where clause.

This is called static scope.

For example, in the following expression,

Ø       E0 = E where

Ø         E = F1 where

Ø           F1 = Expr1;

Ø           F2 = Expr2;

Ø           F3 = Expr3;

F1, F2, F3 are only visible in E, Expr1, Expr2 and Expr3.

It is illegal to use F1, F2 or F3 in E0.

Nested where

An expression followed by a “where” clause is nothing but another expression. As an expression, it can be a sub-expression of a bigger expression, particularly, it can be nested inside another “where” clause.

 

 

For example:

Ø       f 2 = g1 2 + g2 2 where

Ø         g1 a = add a 1 where

Ø           add a b = a+b;

Ø         ;.  //line 4

Ø         g2 a = a+1;

6

Although “;” is a seperator, not a terminator, which means it is optional for the last definition in a where clause, it is mandatory in the case of nested where clause to disambiguate.

 

If we omit the “;” on line 4, jaskell will think the function g2 is only defined for the nested where clause, and hence only visible within the scope of function g1. And that is not what we wanted.

 

 

Declaration Order

The order of the definitions in a where clause is not important. You can define functions and variables in any order. It is legal to reference a function/variable defined after the current definition.

For example:

Ø       max 1 2 where

Ø         max a b = if bigger a b then a else b;

Ø         bigger a b = a > b;

   2

 

Lazy Evaluation

If definition order is not important, which definition gets evaluated first?

What will be the result of the following code?

Ø       say_hello where

Ø         say_hello = println “hello”;

Ø         say_bye = println “bye”;

hello

 

Don’t worry about what “println” is. For now, let’s just assume it is a function that prints message to the standard output.

 

You may be wondering “where is ‘bye’”?.

 

The reason why we don’t see “bye” is because jaskell is a lazy language. Expressions are not evaluated until it is actually used.

 

In the above code, only print “hello” needs to be evaluated in order to evaluate say_hello. say_bye is not requied and hence “bye” is not printed.

 

Because of lazy evaluation, there’s actually no difference existing between a variable and a function with 0 parameters.

In java, we know say_hello() and say_hello are different. The first one is a function call, it is evaluated every time it is called. The second one is a variable whose value is already evaluated. But jaskell does not make such distinction.

 

 

Recursion

Function definition allows recursion. i.e. A function definition can reference itself; two functions can reference each other, no matter who is declared first.

The following factorial function recursively calls itself and calculates n!.

 

Ø       fact 5 where

Ø         fact n = if n==0 then 1 else n*fact(n-1);

120

 

Jaskell even allow you to define such function:

Ø       infinite = infinite

In this case, evaluating infinite will result in infinite loop. But it is still valid if you define such function but never evaluate it.

 

 

Side effect

Jaskell is an impure functional language. One big difference between jaskell and pure functional languages such as haskell is, it allows side-effect.

 

“Side-effect” includes all IO actions and any state change.

 

Side-effect in Jaskell comes when calling Java methods, or when mutable data structures such as Java array or function ref is used.

 

Now, without going through the details of invoking Java, let’s assume we’ve defined a println function that prints message to the standard output. Thus, function println is a function with side-effect.

 

The sequencing operator “>>”

When side-effect is permitted, evaluation order suddenly becomes important. Printing “hello” then printing “world” is obviously different than print “world” then printing “hello”.

 

As we have shown, the definitions in a where clause are evaluated lazily. Who executes first depends on who is evaluated first.

 

But who is evaluated first?

 

For the built-in operators such as “+”, “-“, “*”, “/”, left operand are evaluated before the right operand gets evaluated.

 

But still, in some cases, we may want to specify order manually.

 

Operator “>>” is used to specify evaluation order.

 

Ø       expr1 >> expr2

The above code is an expression, which, when evaluated, expr1 is evaluated first, and then expr2 is evaluated and finally the result of expr2 is returned.

 

For example, to print “hello” then print “world”, we can say:

Ø       println “hello” >> println “world”

hello

   world

 

Call-by-need

 

In the “Lazy Evaluation” section, we explained that functions in where clause are evaluated lazily. The function body is evaluated everytime the function is called (which means, the function call is evaluated), even if the function has not parameter.

So, in the following code, “hello” is printed twice.

Ø       say_hello >> say_hello where

Ø         say_hello = println “hello”;

hello

   hello

 

What about the following expression?

Ø       repeat say_hello where

Ø         repeat a = a >> a;

Ø         say_hello = println “hello”;

hello

The result may be a bit surprising. Function repeat did not really do what we wanted it to do. It only prints “hello” for once.

The reason for this is:

Function call in jaskell is call-by-need. That means, each function argument is evaluated for at most once. Jaskell will save the result from the first execution and return the same result for the subsequent calls.

For the above code, say_hello is actually only evaluated once. The next evaluation of “a” only gets the return value of the last execution and does not print anything.

Jaskell uses call-by-need for performance concerns. And it is negligible if you stay away from side-effects and stick to pure functional style.

 

But, hey, we cannot say that you will never need to do such thing. In case you really need to implement such repeat function, here’s what you can do to work around it:

 

Ø       repeat run_say_hello where

Ø         repeat f = f [] >> f [];

Ø         run_say_hello _ = say_hello;

Ø         say_hello = println “hello”;

hello

   hello

[] represents an empty list. We’ll cover the details in the “list” section.

The run_say_hello function now accepts one parameter. Although it does not use the parameter, when passed as a function argument, it only evaluates to a function, and hence will not stop you from calling it twice and print twice.

 

 

Let – in

However flexible lazy evaluation is, sometimes, we may still want to strictly evaluate an expression.

The let statement can be used in this case.

Assume function readline reads a line from a file, function writeline writes a line to a file, the following code reads one line from file1, one line from file2, and write the two lines to file3.

Ø       let line1 = readline file1; //line 1

Ø         line2 = readline file2;  //line 2

Ø       in

Ø         writeline file3 (line1+line2)  //line 3

 

 

Similar to where clause, let clause can also be used to define functions and variables.

The syntax of defining functions and variables are exactly the same as in where clause.

 

Variables defined by let clause are strict. i.e. They are eagerly evaluated when defined.

In the above code, file1 is read when line 1 is interpreted; file2 is read when line 2 is interpreted.

 

Unlike where clause, declaration order of the definitions in let clause is important. Whoever defined first is executed first.

 

This also means, variables and functions defined earlier cannot reference variables and functions defined after them.

 

Another difference between let clause and where clause lies in how they handle duplicate names. In the same where clause, two functions acannot share the same name. But let clause allows reusing the same name.

In imperative languages such as Java, it is nornal to assign different values to the same variable at different time.

Similar effect can be achieved in jaskell by reusing the same name in let clause.

Ø       let

Ø         var = 1;

Ø         var = var+1;

Ø       in

Ø         var

2

This does not introduce side-effect though. By re-binding a different value to the same name, we are actualy creating a new binding with the same name, shadowing the old name. But the value of the old binding is not changed. The following example illustrates this point:

Ø       let

Ø         fun1 a = a+1;

Ø         fun2 x = fun1 x;

Ø         fun1 a = a*2;

Ø       in

Ø         fun2 10

11

This shows the fun1 referenced by fun2 is still the old value after the name “fun1” is re-bound to a different value. If the value of fun1 referenced in fun2 was changed, the result would have become 20.

 

Let clause cannot define recursive function either.

 

I just said the syntax of defining functions in let clause is the same as that of where clause. Well, I lied. The reserved “_” character can be used in place of a function/variable name when we care only the side-effect of an expression, not its return value.

Ø       let a = action1;

Ø         _ = action2;

Ø         b = action3;

Ø       in

Ø         f a b

 

In the above code, return value of action2 is ignored. But it is guaranteed that action2 is executed between action1 and action3.

 

Similar to where clause, let clause and the expression after keyword “in” is an expression too. As an expression, it can be used anywhere an expression is expected. This also means let clause can be nested.

 

 

Higher order function

Functions in jaskell are fist-class citizen. They can be passed into another function as arguments, they can be returned from any function.

The following function takes as parameter another function, and calls the function. It is also refered to as a call-back in languages such as C++ or Java.

Ø       apply f a = f a

 

The follwing function takes two functions as parameters, returns a new function. The new function, upon receiving argument, will call function f2, and then use the return value of f2 as argument to function f1, it finally returns the return value from function f1.

Ø       compose f1 f2 = new_f where

Ø         new_f a = f1(f2 a)

This function represents the basic function composition.

Lamda Function

Jaskell supports lamda function. A lamda function is an anonymouos function. It allows us not to name a function when it is too trivial to create a named function.

For the function composition example, we can rewrite it using lamda function as follows:

Ø       compose f1 f2 = \a->f1(f2 a)

Character ‘\’ is used to indicate a lamda function. Parameter names are listed after ‘\’ and before ‘->’. The expression after ‘->’ is the function body.

 

For the following compse2 function:

Ø       compse2 f1 f2 = new_f where

Ø         new_f a b = f1(f2 a b)

A lamda function version will be:

Ø       compose2 f1 f2 = \a b->f1(f2 a b)

 

Currying (Partial application)

 

Jaskell supports partial function application. If a function is not given the number of parameters declared, the function body is not evaluated. Instead, such partial application results in a new function that expects the rest of the parameters.

 

For example, function “add a b c = a+b+c” expects three parameters.

If we simply evaluate “add” without giving it any argument, “add” is a function expecting three parameters.

If we evaluate “add 1”, it will be a function expecting parameter b and c; argument 1 is remembered and bound to parameter a.

 

If we evaluate “add 1 2”, it will be a function expecting parameter c; argument 2 is remembered and bound to parameter b.

 

Conceptually, calling add 1 2 3 is no different than calling ((add 1) 2) 3.

 

We can say add 1 2 3 is a function call with 3 arguments. At the same time, we can also say it is three subsequent function calls with 1 argument each.They are exactly the same thing.

 

For the compose function defined earlier, it can be rewritten as:

Ø       compose f1 f2 a = f1 (f2 a)

Because of currying, it is no different than

Ø       compose f1 f2 = \a->f1(f2 a)

 

Operator as function

Jaskell operators can be treated as functions and used in a prefix syntax.

 

Simply enclosing an operator in a pair of parenthesis will make it a function.

 

The following add function:

Ø       add a b = a+b

can be simplified as an operator function: (+)

Ø       (+) 1 2

   3

The ‘$’ operator introduced earlier, is a function application operator. If used as a function, is exactly the same as the apply function.

Ø       apply (add 1) 2 where

Ø         apply f a = f a

3

Ø       ($) (add 1) 2

3

 

 

Function as operator

An operator can be used as a function in a prefix syntax. Similarly, a function can be used as an operator in an infix syntax.

 

The syntax is simple. Enclosing an expression within a pair of backquote (`), will make it an infix binary operator.

 

Ø       1 `add` 2 where

Ø         add = (+)

3

a `f` b is equivalent to f a b.