Print
About Functions

In this page, we'll use Jaskell Shell to run the demos, the '>' before each line is the shell's prompt; the "=>" is the shell's indicator of the expression value.

Function Basics

As in other functional programming languages, function is about everything in Jaskell. All the operators are function (Do you know that the '$' operator is just a function with special precedence rule?); 'try' is function; 'switch' is function; 'typecase' is function; 'new' is function; 'import' is function; 'instanceof' is function.

So the very basic skill one has to grasp is how to define a function.

Theoretically, there's just one way of defining function. For example:

> add a b = a+b
> 
=> add(,)

And it can be called with the following syntax:

> add 1 2
> 
=> 3

Jaskell also supports the Java style function definition syntax by enclosing formal parameters between a pair of parenthesis and delimit them with a ",". The "add" function can be written as:

> plus(a,b)=a+b
> 
=> plus(,)

The above code is exactly equivalent as the definition without parenthesis.

Funciton invocation syntax can also be with or without parenthesis, regardless whether the function is defined with or without parenthesis. For example:

> add(1,2)
>
=> 3
> plus 1 2
>
=> 3

All jaskell functions are curried, no matter if it is defined with parenthesis or not. That means: if "add" is a function expecting two parameters, "add 1" or "add(1)" or "1->add" is a function expecting the remaining one parameter.

Disallow Currying

Function currying, as handy as it is, makes it easier to introduce bugs where we thought we provided enough parameters to fire off a function, but the function never runs because one parameter is missing.

And yes, by theory, all functions in jaskell are curried. There's no way one can turn off function currying. But we can always use a list as the function parameter. Through the pattern match scheme, such function will look like a non-curried function:

> add[a,b]=a+b
>
=> add()

When using this function, we have only two options:

  1. curry it by using the function name only.
  2. provide a list with exactly two elements, like:
    > add[1,2]
    >
    => 3

Probability of making unnoticeable mistakes is much lower.

Parameter passed by name

Function parameters in Jaskell are all passed by position, as they are in Java. This is handy with small functions like "add". "add 1 2" is simple and straight forward. It may become unwieldy if the number of parameters scales up. For a function with 5 or more parameters, passing actual arguments by position becomes hard to write and read. Even worse, it is impossible to support default parameter value.

Again, bad news first. Jaskell the language doesn't provide a way of passing parameter by name, there's no language support for default parameter value either.

The good news is, we can use a tuple together with pattern matching to mimic a pass-by-name syntax:

> rename {dir, from, to, recursive} = //rename files in dir ......
>
=> rename()

Formally speaking, this function expects one tuple parameter only. But syntax-wise, it can be read as a function expecting 4 parameters passed by name.

A typical syntax to call it is:

> rename {dir=".", from="*.bak", to="*.java", recursive=true}

Formally, we are passing a tuple parameter, but the syntax can be read as passing 4 parameters by name.

Default parameter value

As for default parameter value, the "jaskell.prelude" namespace has a function "default" that does exactly what we want. For example, we may want to make the "dir" parameter default to ".", and the "resursive" parameter default to false. We can re-write the 'rename' function as:

> rename args@{from,to} = default args {dir=".",recursive=false} $
>   //do the renaming here ...
>

Now only the 'from' and 'to' parameters are mandatory. 'dir' and 'recursive' are optional and have default value. And it can be called as:

> rename{from="*.bak",to="*.java"}
>

The omitted parameters will take the default value.

Side note: The '@' symbol is used in pattern match to give a certain pattern a name.

Overloading

Bad news, Jaskell has no language support for function overloadin.

Good news, pattern match is an alternative.

For example, we could support both pass-by-position and pass-by-name for the same function by using pattern matching:

> add[a,b] = a+b
> |  {a,b} = a+b
>
=> add()

There are of course other more valuable usages of pattern match. Indeed, the syntax for calling Java method uses the list parameter syntax to deal with Java method overloading.

So, instead of "obj.f(a,b)", we call "obj.f[a,b]".

Overload by type

In Java, we are able to overload by parameter types, i.e. f(String, int) is different than f(int, String). The same thing can be done in Jaskell using the 'jaskell.prelude.overload' funciton. For example:

> f a b = overload[a,b]
>   .case([String,int], expression_for_string)
>   .case([int,String], expression_for_int)
>   .end
> 
=> f(,)

Or,

> f arg@[a,b] = overload arg
>   .case([String,int], expression_for_string)
>   .case([int,String], expression_for_int)
>   .end
=> f()

See Function Library for complete function reference.

Powered by Atlassian Confluence