Channels ▼
RSS

JVM Languages

Scala for C# Developers: The Magic


In the second installment of this three-part  tutorial, I explained the difference between mutable and immutable variables in Scala, and I showed the unusual way in which Scala operators can be modified. In this final article of the series, I focus on the way Scala works with expressions and pattern matching, the powerful implicit conversions, and Scala's smart way of avoiding problematic nulls. Finally, I dive into the powerful ways in which you can use Scala traits.

Working with Expressions Instead of Statements

Programming languages provide statements that execute code and don't return a value. Programming languages can also evaluate expressions that return values. In order to evaluate an expression, code must be executed; therefore, both statements and expressions require the execution of code. So the real difference between a statement and an expression is that the latter returns a value.

Scala provides an if clause, and you can build if blocks. In C#, the if clause allows you to execute different statements according to the two possible results of evaluating a Boolean expression: true or false. In Scala, an if block is an expression, so the if clause evaluates a Boolean expression and returns one expression or another depending on the result of the evaluations: true or false. An if block in Scala evaluates to a value.

As I explained in my previous article, the Scala Console in the Scala IDE makes it really easy to evaluate expressions and check the results. Let's move straight to the code and run some examples in the Scala Console. Enter the following expressions after the scala> prompt, and the Scala Console will provide the results.

scala> val myResult = 300
if (myResult > 300) "More than 300" else "Less than or equal to 300" 
myResult: Int = 300
scala> res0: String = Less than or equal to 300

As you might notice, the Scala if expression works the same way as the ?: syntax in C#. The following lines of code use the ?: syntax to produce a similar result in C#. In addition, it would be necessary to return myString in the method in which the code is inserted.

const int myResult = 300;
var res0 = myResult > 300 ? "More than 300" : "Less than or equal to 300";

In Scala, there is no need for the ?: syntax because of the way the if expression works; therefore, you will always use the if expression whenever you want to evaluate a Boolean expression. This makes code easier to read and reduces unnecessary boilerplate.

Working with Pattern Matching

The following C# code shows a simple example of a method that performs a pattern match and returns a message string based on the received parameter:

public string CreateMessage(int code)
{
    var message = string.Empty;
    switch (code)
    {
        case 100: message = "One hundred";
            break;
        case 200: message = "Two hundred";
            break;
        case 300: message = "Three hundred";
            break;
        default: message = "Unknown value";
            break;
    }
            
    return message;
}

The following Scala code defines a CreateMessage method for an ErrorHandler object that includes a simple pattern match. The pattern match uses the match keyword and defines the only expression for the method that returns a String. Notice that the code is simpler, easier to read, and doesn't require a temporary variable to return the result of the pattern match. As explained with the if expression, the pattern match is an expression and returns a String.

object ErrorHandler {
	def CreateMessage(code: Int): String = code match {
            case 100 => "One hundred"
            case 200 => "Two hundred"
            case 300 => "Three hundred"
            case _ => "Unknown value"
        }
}

The code is concise and expressive. The underscore works as a wildcard, so if the other case expressions don't match the value for code, the pattern match will reach the case _ expression and will return "Unknown value." The case _ is the equivalent to default in C#. In Scala, you must use => for pattern match as in the example code, but you can also use => for defining closures.

The following lines show different ways of calling the CreateMessage method and the results in the Scala Console. Notice that you can call the method without using either the dot (.) or the parenthesis.

scala> ErrorHandler.CreateMessage(100)
res0: String = One hundred

scala> ErrorHandler CreateMessage 200
res1: String = Two hundred

scala> ErrorHandler CreateMessage 450
res2: String = Unknown value

Working with the scala.Option Class Instead of null

Scala provides the scala.Option class in the standard library as a container of something or nothing, thereby, discouraging the use of null. Two elements in Scala extend scala.Option:

  • Some[T] is a class that represents some existing values of type T.
  • None: is a case object that represents nonexistent values. It's an empty container.

The following lines of code show a new version of the sample CreateMessage method defining the code parameter as an Option[Int]; thus, the code might have either a Some[Int] container or the None empty container. The pattern match works with the result of code.getOrElse(0). Because the code parameter is defined as an Option[Int], it provides the getOrElse method defined in Option , which retrieves the existing value in the container or returns the value received as a parameter when the Option is representing an empty container (None).

object ErrorHandler {
	def CreateMessage(code: Option[Int]): String = code.getOrElse(0) match {
            case 100 => "One hundred"
            case 200 => "Two hundred"
            case 300 => "Three hundred"
            case _ => "Unknown value"
        }
}

The following lines show how you can define a mutableCode variable as an Option[Int] and initialize it with an empty container (None). When you call the CreateMessage method with mutableCode initialized to None, code.getOrElse(0) will return 0, so the result will be "Unknown value":

scala> var mutableCode : Option[Int] = None
mutableCode: Option[Int] = None

scala> ErrorHandler CreateMessage mutableCode
res0: String = Unknown value

The next lines change the value of mutableCode to a container that includes the Int value 100, Some(100). This time, when you call the CreateMessage method with mutableCode initialized to Some(100), code.getOrElse(0) will return the contained Int value, 100; therefore, the result will be One hundred.

scala> mutableCode = Some(100)
mutableCode: Option[Int] = Some(100)

scala> ErrorHandler CreateMessage mutableCode
res1: String = One hundred

Option also provides a get method that attempts to access the value stored in the container and doesn't require any parameters. If you call get for an empty container (None), it throws an exception. According to your needs, you can use call get or getOrElse to retrieve the value in the container.

Named and Default Arguments

Scala 2.8 introduced two new features related to arguments that are similar to their counterparts in C#:

  • Named arguments, also known as named parameters. You can specify the arguments based on the parameter names instead of the parameters positions.
  • Default arguments, also known as default parameters. You can specify default values for method parameters.

A simple example will be enough to show you the Scala syntax for default arguments. The following lines define default values for the Point3D class parameters and for the move method.

class Point3D(val x: Double = 0, val y: Double = 0, val z: Double = 0) {
    def move(dx: Double = 2, dy: Double = 1, dz: Double = 0.5) : Point3D = 
        new Point3D(x + dx, y + dy, z + dz)
}

This way, you can create a new instance of Point3D without parameters, and the initial values for x, y, and z will be 0:

val myPoint3D = new Point3D()

You can use named parameters to specify the value for just the z parameter, and the values for the variables will be 0 for x, 0 for y, and 10 for z.

val myPoint3D = new Point3D(z = 10)

You can call the move method without parameters and it will use the default values:

myPoint3D move()

Implicit Conversions with Tuples

Scala supports tuples that combine a fixed number of elements, which you can pass around as a whole. A tuple can include elements with different types. The following code defines a tuple with an Int, and two strings using the shortcut that enables you to create a tuple by enclosing the elements in parentheses.

val tupleWith3Elements = (10, "String1", "String2")

Because the previous line defines a tuple with three elements, the actual type of the tuple will be Tuple3[Int, String, String]. The following line is equivalent to the previous one, but it does not use the nice syntactic sugar:

val tupleWith3Elements = new Tuple3(10, "String1", "String2")

To access the elements of the tuple, you can use the different methods that start with an underscore (_) and continue with the element number, starting in 1. For example, tupleWith3Elements._1 returns 10, and tupleWith3Elements._2 returns "String1," and so on.

As you might expect, if you create a tuple with two String elements, the type of the tuple will be Tuple2[String, String]. If you create a tuple with four Int elements, the actual type of the tuple will be Tuple4[Int, Int, Int, Int].

A Tuple3[Double, Double, Double] would be good for holding the three values (x, y, and z) of Point3D. In addition, a Tuple3[Int, Int, Int] would be useful when the three values are integers. If someone asks you to allow the sum (+) method defined in the Point3D class to work with a Point3D, a Tuple3[Double, Double, Double], or a Tuple3[Int, Int, Int], your first idea might be to create three sum (+) methods — each with a different type in the parameter. However, if you do this, you will be creating three blobs of  very similar code. Scala provides an elegant feature that enables you to use a single method and automatic type conversions to reduce this duplicate code: implicit conversions. With the usage of implicit conversions, you need only define a single piece of code for the sum (+) method in the Point3D class, and then declare the implicit conversions from the different tuples to Point3D. The following code shows the sum (+) method that receives a Point3D as a parameter and returns a new Point3D with the result of the sum operation:

class Point3D(val x: Double = 0, val y: Double = 0, val z: Double = 0) {
    def +(that: Point3D) : Point3D = 
        new Point3D(x + that.x, y + that.y, z + that.z)
}

The following code defines a new Point3DImplicits object with two implicit conversion methods that start with the implicit keyword and convert from Tuple3[Double, Double, Double] to Point3D and from Tuple3[Int, Int, Int] to Point3D.

object Point3DImplicits {
      implicit def Tuple2Point3D(value : Tuple3[Double,Double, Double]) = 
         new Point3D(value._1,value._2, value._3) 
      implicit def IntTuple2Point3D(value : Tuple3[Int, Int, Int]) = 
         new Point3D(value._1,value._2, value._3)
  }

Now, you just have to make sure you add the following line to import the Point3DImplicits methods so that Scala can perform the necessary type conversions:

import Point3DImplicits._

If you write the following lines, Scala won't find a sum (+) method that accepts a Tuple3[Int, Int, Int], but it will find the method defined in Point3DImplicits that can perform the conversion from Tuple3[Int, Int, Int] to Point3D. Scala will use that conversion method and, once it has the Tuple3[Int, Int, Int] converted to a Point3D, Scala will use it to call the sum (+) method. The same thing happens when you call the sum (+) method with a Tuple[Double, Double, Double].

val myIntTuple = (5, 10, 15)
val myPoint3D = new Point3D(10, 8, 3)
val myNewPoint3D = myPoint3D + myIntTuple

val myDoubleTuple = (5.2, 10.1, 15.4)
val myNewPoint3D2 = myPoint3D + myDoubleTuple

Obviously, there is an overhead introduced when Scala has to apply the implicit conversion, so it isn't a good idea to abuse the definition of implicit methods. However, implicit conversions are great for reducing the unnecessary duplication of code that performs the same action.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video