Tuesday, October 19, 2010

Scala Tutorial Part 2 - Defining And Calling Functions

Scala is a hybrid language that combines features of object oriented languages with features of functional languages. In this third part of my scala tutorial series i am going to show how to define and call functions in scala. All code snippets shown here can be run using the interactive scala shell or the scala interpreter as shown in the first part of this series.

Functions Without Parameters

The following snippet defines a function that takes no parameters and does not return a value:
scala> def sayHello() { 
     | println("hello")
     | }
sayHello: ()Unit
The scala shell determines that sayHello is a parameterless function that has the return type Unit. The type Unit indicates, that a function does not return a value. For now you can think of Unit as an equivalent of Java's void.
A function definition starts with the keyword def. This is followed by the function name and empty parentheses. Function arguments would go between the parentheses. This is followed by the function body which is surrounded by square brackets.

Functions that return a value have an equals sign between the parameter list and the method body:
def giveUniversalAnswer() = {
     | 42
     | }
giveUniversalAnswer: ()Int     
A function always returns the value of the last statement in the function body. You can mark this by using the return-statement, but this is not usual in scala. As you can see the type inference system determined that this function returns a value of type int. Sometimes the type inference algorithm is unable to infer the return type of a method (e.g. the scala compiler cannot determine the return type of recursive methods). In this case you have to declare the return type explicitly. And of course in some cases it makes sense to declare the return type of a function for the sake of readability.
The return type has to be declared in front of the equals sign:
def giveUniversalAnswer(): Int = {
     | 42
     | }
giveUniversalAnswer: ()Int     
If you omit the equals sign after the parameter list, the function does not return a value. This is even true for functions that have a return-statement.


Command Query Separation

It is good practice to differentiate between functions that have side effects and functions that are free of side effects. A side effect is an operation that changes the state of a program, for instance by changing a global variable or a type field. The practice of differentiating between these function types was introduced by Bertrand Meyer in his book "Object Oriented Software Construction". He introduces the term command query separation for this differentiation.
The basic idea of command query separation is do differentiate between these two function types:
  • queries: functions that compute values but are free of side effects
  • commands: functions that change the state of the system but do not return values.

// Side note:
// A more in depth description of command query separation can be found
// in Martin Fowlers Bliki.

In scala you can use functions without return type to implement commands and functions with return type to implement queries. This way you have a syntax for declaring queries and a syntax for declaring commands.


Functions With Parameters

The following snippet defines a function that takes two arguments:
scala> def registerPerson(name: String, password: String) {                    
     | printf("User '%s' added to database with password '%s'", name, password)
     | }                                                                       
registerPerson: (name: String,password: String)Unit
The parameters of a function go between the parentheses after the function name. Unlike the return type of a function, the type of function parameters has to be declared. The function parameters are constants that cannot be modified inside the function body. The following code will not compile:
scala> def registerPerson(name: String, password: String) {                    
     | name = "not allowed"
     | }
:6: error: reassignment to val
       name = "not allowed"

Calling Functions
Functions are called by the function name followed by the parameter list. If the parameter list is empty you are allowed to omit the empty parentheses. The following code shows two legal ways to call the function sayHello defined above:
scala> sayHello()
hello

scala> sayHello 
hello


// Side note:
// As we will see later, the parentheses can also be omitted
// when calling type members that have only one parameter.

When calling a function that takes parameters, the parameters are passed to the function in parentheses. To call the function registerPerson type
scala> registerPerson("Helmut", "secret")                  
User 'Helmut' added to database with password 'secret'
To make this code easier to read, scala 2.8 introduced the notion of named parameters. These allow you to specify the name of the parameter in the function call. This makes the function call above easier to read and less error prone. Since the name of the parameters is specified you are even allowed to change the order of the parameters passed. The following code demonstrates this:
scala> registerPerson(password="secret", name="Helmut")
User 'Helmut' added to database with password 'secret'


Uniform Access

When declaring functions without parameters, the parentheses indicating the empty parameter list can be omitted. This makes it possible to make the access to a computed value look like the access to a property. When the parentheses for the empty arguments are omitted in the function definition you also have to omit them when calling the function:
scala> def universalAnswer = {     
     | 40 + 2
     | }
universalAnswer: Int

scala> var x = universalAnswer * 2
x: Int = 84
This example does not make a lot of sense. But as we will see later, this feature allows you to define type members that perform a computation but look like fields for clients.


Summary

In this post I gave a short introduction to defining functions in scala. Below is a summary of rules for the use of functions in scala:

  • omit the equals sign in front of the function body if the function does not return a value
  • use an equals sign for functions that return a value, otherwise the return-value is being ignored
  • use command query separation
  • function arguments are unmodifiable constants
  • you can omit the parentheses when calling functions with zero arguments
  • if the parentheses for an empty parameter list is omitted in the function definition, you have to omit it when accessing the function

I hope this article gave you an insight in using functions in scala. Comments, suggestions for improvement or questions are greatly appreciated.

No comments:

Post a Comment