What do C ++ developers think of Scala

Arno Haase

Scala is an elegant, expressive programming language that has recently become increasingly popular and widely used. It can be easily integrated with Java and the .NET platform and, as a "General Purpose Language", is suitable for all tasks for which Java or C # are otherwise used.

Background and framework

Scala is developed and maintained by Martin Odersky and his working group at the Ecole Polytechnique Fédérale de Lausanne. You can clearly see that the language is designed by people who have a wealth of experience in programming languages. At its core, the language consists of relatively few, clear concepts that interact elegantly and leave a "rounded" impression. The first release came out in 2003, the second, revised version in 2006, the current version is 2.7.3. Scala is well documented and a number of good sources can be found on the Internet [1] [2] [3].

The range of tools around Scala - compilers, interpreters, IDE plug-ins - is so mature that the language is suitable for the rough everyday life in "real" projects. Scala is compiled in bytecode and there are compilers for both the Java and .Net platforms. The project sees the two platforms as strategic and supports them well, with a slightly greater affinity to the Java world.

One of Scala's strengths is that it has an explicit language specification - this sets it apart from many other languages ​​like Groovy. Scala includes a rich selection of standard libraries which, in addition to collections, include a powerful concurrency framework in the Erlang style based on an actor model. In addition, Scala programs can use all libraries and frameworks of the respective platform (Java or .Net).

The lift framework that is based on Scala deserves a special mention. It is a robust, high-performance web framework that combines the scope and expressiveness of Rails with static type checks, seamless integration with Java libraries and the performance of Java.

A first overview

Enough of the preface - to the basic ideas of the language: Scala is functional, object-oriented, expandable and strongly typified:

Functional: In Scala, functions are full-fledged language elements; they can be stored in variables or passed as parameters. There are closures (anonymous functions that remember the context of their definition), and the functions can be partially evaluated. In Scala there are no statements, everything is an expression, so it has a type and a value. This is a conceptual simplification compared to imperative languages ​​like Java or C #. Programming without side effects is easy and comprehensible in Scala, for example there are collection classes with no side effects. But this should not be seen as dogmatic, because one can work pragmatically with changeable variables if that is desired.

Object-oriented: In Scala everything is an object. For example, you can call methods on the number 1, and the fragile mechanism of autoboxing / unboxing is no longer necessary. Arrays are also real objects without any special treatment in the language. Under the surface, primitives and arrays - where applicable - can be mapped to the efficient constructs of the respective runtime environment, so that this uniform programming model brings only minor performance losses.

Expandable: You can write libraries in Scala which, when used, appear almost like "built-in" language features. There is the rule that a method can be called with exactly one parameter in infix notation without periods and brackets. A good example is the method that is defined on Int, expects it as a parameter and returns an iterator. This allows you to run a loop over a range of numbers without having to use special syntax:

for (i <- 1 to 10)
println (i)

You could write instead - is called here as a normal method, including a preceding period - but the infix notation reads better. In addition, there are hardly any restrictions as to which characters are to be used in method names. "+" or "*" are completely normal method names in Scala, and you can define arithmetic operations for your own data types that look exactly like those of Int or Double.

Strongly typed: Every variable in Scala has a precisely defined type that the compiler determines - just like in Java. If you call an operation that is not defined for this type, the compiler will complain. However, Scala recognizes the type of a variable from the value that initializes it ("Type Inference"). The keyword defines a variable:

var x = List (1, 3, 99) // x is of the type List [Int]
x = "Hello" // Compile error
x = List ("a", "b") // also compile errors

Scala recognizes a number of errors when compiling or in the editor of the IDE without having to specify the types redundantly as in Java.

Specifying the return types of methods is optional in Scala, at least if the methods are not recursive. The keyword defines a method, and the types of the parameters are in Scala separated by a colon after the variables:

def plus (a: Int, b: Int, c: Int) = a + b + c

These code snippets show that Scala code can be written quite compactly. A semicolon at the end of the line is optional, and method definitions do not need curly braces if they consist of a single expression. The value of the last expression of a method can be returned without an explicit "return". As a result, the definition of a small auxiliary method is just as compact as the introduction of an auxiliary variable. The analogy between variable and method definitions can also be seen in the syntax: The implementation is "assigned" to a method with an equal sign, just like the value of a variable.

Concept of traits

Inheritance and Traits

Scala is object-oriented. There are classes that inherit from other classes, at most one at a time. In this, Scala does not differ from many other languages. In addition to the classes, Scala also has so-called traits (properties). Like classes, they can contain methods and variables. Traits can inherit from classes, classes from traits, traits from traits. But - and this is what is special - a class can inherit from several traits. Before this multiple implementation inheritance triggers knee-jerk defense reactions in memory of the complexity of C ++: Scala maps traits internally through delegation and thus elegantly avoids the well-known problems of multiple inheritance. But more on that in a moment.

First of all, compared to Java interfaces, for example, traits can provide a wide range of methods without each class having to re-implement them. For example, a trait that allows objects to be sorted essentially needs a single method of comparison. This is abstract, so every implementing class must provide it:

// minimal version
trait Ordered [T] {
def compare (other: T): Int
}

This corresponds to the Comparable interface of Java and is everything that you need to sort objects, strictly speaking. The one in square brackets is a type parameter. In Scala, the trait can also provide comparison operators that are defined on the basis of the method:

// more detailed version
trait Ordered [T] {
def compare (other: T): Int

def <(other: T) = (this compare other) <0
def> (other: T) = (this compare other)> 0
def <= (other: T) = (this compare other) <= 0
def> = (other: T) = (this compare other)> = 0
}

The trait has implementations for the comparison operators, and any class that implements them inherits them. Without traits, for example in Java, you have three options, all of which are less than satisfactory. Either you limit yourself to one method in the interface - this reduces the convenience for calling code, or you include the convenience methods in the interface - then each class has to implement them anew. Or you can create an abstract class from the interface, but then the comparison can no longer be mixed with inheritance. With traits, on the other hand, callers can be offered a broad, rich interface without doing additional work for implementing classes.

Combinable modifications with traits

But traits can do even more. After all, a class can inherit from multiple traits, in this way their behavior can be combined. To make this clear, you define a simple queue of whole numbers [3]:

abstract class IntQueue {
def get (): Int
def put (o: Int)
}

A simple implementation could look like this:

import scala.collection.mutable.ArrayBuffer

class SimpleIntQueue extends IntQueue {
private val buf = new ArrayBuffer [Int]
def get () = buf.remove (0)
def put (o: Int) = buf + = o
}

Assume now that you want queues with special behavior:

  • Doubling: The queue doubles all numbers put in.
  • Filtering: Negative numbers are ignored.
  • Incrementation: The queue always returns a number that is one larger than the one you put in.

A special queue class can be written for each of these cases. Since combinations are also conceivable and the sequence is important, the number of possibilities explodes even with such a small number of variants. Instead we can define traits:

trait Doubling extends IntQueue {
abstract override def put (o: Int) =
super.put (2 * o)
}

The details of the syntax are way beyond what this article can do. Therefore, he only focuses on the core of the traits, the call to. The trait inherits from - but the method in is abstract. Calling them up doesn't make sense. The answer lies in the fact that Scala arranges the traits for each class in a list (linearization), at the end of which the class itself stands. When calling a method, the implementation of the first trait is to be used. It can pass the call on to the next entry in the chain:

val queue = new SimpleIntQueue with doubling
queue.put (10) // goes to Doubling.put
println (queue.get ()) // returns 20

The implementations of the other two traits behave analogously, they also delegate calls to the next entry in the linearized chain:

trait incrementing extends IntQueue {
abstract override def put (o: Int) =
super.put (o + 1)
}

trait filtering extends IntQueue {
abstract override def put (o: Int) =
if (o> = 0) super.put (o)
}

Now the behavior of a queue can be controlled by combining the traits:

val queue = new SimpleIntQueue
with filtering, doubling, incrementing
queue.put (-1)
queue.put (-10)
queue.put (2)

println (queue.get ()) // prints 0: (-1 + 1) * 2
println (queue.get ()) // prints 6: (2 + 1) * 2

The order of the traits is important - they are essentially applied from right to left. A great strength of the traits is their combinability. It is based on the fact that it is not fixed once and for all, but delegated to the next element in the linearized chain. This makes traits flexible. And for those readers who are concerned about the complexity of multiple inheritance, here is another explanation: Because the inheritance of traits can be handled internally via the linearized chain, there can be no ambiguity if several traits implementations are available for the same method.

Concurrency

Concurrency with actor

Scala contains a number of useful libraries. As an example, we would like to introduce a particularly beautiful library that also illustrates the expressiveness of Scala: the Actor Library.

Concurrent programs are becoming increasingly important, but unfortunately they are difficult to write correctly. If you sync too much, the performance suffers, and in extreme cases you get deadlocks. If too little is synchronized, you will get inconsistencies in the data. A proven design pattern for robust concurrent programs is the use of actors [4]: ​​the threads communicate with one another via asynchronous messages, and one can understand each actor individually without constantly thinking about concurrency.

In Scala there is the class that provides this function. All you have to do is inherit application code from it:

import scala.actors.Actor

object MyActor extends Actor {
def act () {
while (true) {
receive {
case "Hello" =>
println ("Greetings back!")
case x: Int =>
println ("A number:" + x)
case msg: String =>
println ("The message was" + msg)
}
}
}
}

This source code uses a number of new Scala language features. First of all, it's not a class, but an object. Scala doesn't know any static variables or methods, but the keyword with which singletons can be defined. The call to the method has a code block, a partial function, as the only parameter. This is the only parameter so that you can leave the brackets out and the whole thing looks like a keyword belonging to the language. The partial function uses pattern matching: It decides which code is to be executed depending on the passed value. This is a generalization of the switch-case construct of C-like languages ​​like Java or C #.

The first case compares the parameter with a constant value - it matches exactly the one string that is there. The second case fits exactly if any int value was passed. It binds this number to the variable x, which is to be used for the output. Finally, the third case deals with strings. The whole thing is in the method that is called when the actor is started. There is an infinite loop so that the actor does not exit after the first message.

So that the actor does something, he has to be started and then messages have to be sent to him:

MyActor.start

MyActor! "Well"
MyActor! 99
MyActor! "Hello"

The exclamation mark as the syntax for sending a message is based on the Erlang programming language. In Scala it is a completely normal method - special characters are allowed in method names, and if a method has only one parameter, the point and the brackets around the parameter are to be left out.

This simple example only scratches the surface of the features of Scala's Actor Library and does not fully explain all the language constructs used. However, it illustrates how expressive Scala code can be and how one can write library code that, when used, acts like built-in language features.

Conclusion

Conclusion

Scala is an elegant, expressive programming language. It is mature, generates efficient code and brings a good integration into the common Java IDEs including support for interactive debugging with [5]. Scala represents a successful, pragmatic combination of functional and object-oriented programming. It contains a large number of its own libraries and can be seamlessly integrated with the Java or .Net platform, so that the existing wealth of frameworks and libraries can be used. The programming language is definitely solid enough for commercial, productive use - and for the fun factor alone it is definitely worth a closer acquaintance.

Arno Haase
is a freelance software architect from Braunschweig. He is one of the founding members of the software engineering podcast se-radio.net. He also speaks regularly at conferences and is the author of various articles and patterns as well as the co-author of books on JPA and model-driven software development. Contact.

Literature & online resources

  1. Scala
  2. Martin Odersky et al. An Overview of the Scala Programming Language (2006)
  3. Martin Odersky, Lex Spoon, Bill Venners, Programming in Scala (2008)
  4. Brian Goetz, Java Concurrency in Practice, Addison-Wesley, 2006
  5. IDE and Editor plugins