ExpandCollapsePrev Next Index

+ 8.1 Simple function definitions

You can define simple calculations easily. We'll use a classic example:

  fun square (x:int) => x * x;

The function square just multiplies its argument x by itself. We can use it now:

  write$ stdout, "Input number: "; fflush stdout;
  val result = readln$ stdin;
  val cleaned_result = strip result;
  val v = int cleaned_result;
  val v2 = square v;
  println$ "Square of " + str v + " is " + str v2;

42

Input number: 42
Square of 42 is 1764

Here:

  • The fun binder introduces the function square.
  • square accepts a single argument of type int.
  • The operator * is used for multiplication.
  • square returns the product of its argument with itself.
  • The type of value returned by square is deduced by the compiler, that type will be int because multiplying an int by another int yields an int.
  • The type int can also be used as a function to convert a string containing only digits to an int.
  • The function str can be used to convert an int to a string.

There is a rule for functions:

A function introduced by a fun binder is not allowed to have any side effects.

The compiler does not enforce this rule, but it does take advantage of it when optimising your code.

+ 8.2 Anonymous function definitions (lambdas)

Functions can be named (as above), or they can be left nameless. The former is beneficial when a function needs to be reused many times, and the latter for situations where reuse is not needed. The distinction is blurred by the fact that nameless functions can be assigned to variables/values. Still, there are many situations where anonymous functions are vital.

FYI: Referring to these as "lambda" functions is unavoidable. Computer science literature (since Alonzo Church, circa 1930) is rife with the term. If you get confused, just remember that it means "a nameless function without side-effects".

The syntax for defining an anonymous function is just like what we use for named functions, we just leave the name out.

  (fun (x:int) => x * x)

Note: At present, Felix understands the code better when parentheses are included around the anonymous function. It helps with scoping issues, but we apologize for having to include them. A future version of Felix may not need them.

+ 8.3 Mapping

Functions are very useful for transforming one value to another. If you need to do the same operation across several values, you should consider using map. Furthermore, lambdas are ideal for one-off transformations.

The following uses of map are equivalent. The first uses a pre-defined function square' (don't forget apostrophes can be part of the symbol name) and the second defines the square function in place.

  var data = list(1,2,3);
  fun square' (x:int) => x * x;
  
  // the following are equivalent.
  println$ unbox$ map square' data;
  println$ unbox$ map (fun (x:int) => x * x) data;

list(1, 4, 9)
list(1, 4, 9)