Scala is a programming language released in 2004 by Martin Odersky. It provides support for functional programming and is designed to be concise and compiled to Java bytecode so that a Scala application can be executed on a Java Virtual Machine (JVM). Let’s check at the core features of the language. Hello World First, let’s see how to implement a hello world in Scala: We defined a object containing a method. This method takes an array of as an input. HelloWorld main String In the main, we called the method which takes an object as an input to print something in the console. println Meanwhile, is also part of the package. HelloWorld io.teivah.helloworld Values We can name the result of an expression using the keyword. In the following example, both expressions are a valid way to define a value: val The type is optional. In the example, and are both typed as a String. v1 v2 The Scala compiler can infer the type of a value without having to explicitly declare it. This is known as type inference. A value in Scala is . This means, the following code is not going to compile: immutable Last but not least, a value can be evaluated using the keyword: lazily lazy In this case, will not be evaluated during its declaration but during its first invocation. context Variables A variable is a value. It is declared with the keyword. mutable var Just like with values, the type is optional. Yet, a variable cannot be evaluated lazily. Furthermore, Scala is a . The following code, for example, is invalid as we try to map an into a variable already defined as a : statically typed language Int String Blocks In Scala, we can combine expressions by surrounding them with . Let’s consider the function which takes an object as an input. The two following expressions are similar: {} println() Note that for the second the last expression ( ) is the result of the overall block. println i + 2 When we call a function with a single argument just like , we can also omit the parenthesis: println Basic Types Scala is considered a pure object-oriented language because . Hence, there is no primitive in Scala (like Java for example). every value is an object int There are 8 basic types in Scala: Byte Short Int Long Float Double Char Boolean Scala type hierarchy Every basic Scala type inherits from . On the other side, is an alias for . Lastly, both and inherits from . AnyVal AnyRef java.lang.Object AnyVal AnyRef Any String Interpolation Scala provides an elegant way to embed variable/value references directly in processed string literals. As a concrete example: This is made possible by the before the quotation mark. Otherwise, it would print . s interpolator Hello $name! There are few interpolators provided by Scala but it is a customizable mechanism. We can create for example our own interpolator to handle JSON conversions like this: . println(json"{name: $name}") Array and List An array is also handled in Scala as an object: Two things to highlight here. Firstly, the way to set elements. Instead of using like in many languages, we use the syntax . This is a syntactic sugar to let us call an object just as . Under the hood, the compiler is calling a default method called taking a single input (an in our case) to make it possible. a[0] a(0) if it was a function apply() Int Secondly, despite being declared as a in this example, the object is mutable so we can change the value of indexes 0 and 1. just enforces to not mutate , not the corresponding object. val Array val the reference An array can also be initialized this way: This expression is similar than above. Moreover, because it is initialized with 5 and 2, the compiler infers as an . a Array[Int] To manage multi-dimensional arrays: This code creates a two-dimensional array and initializes the very first element to 5. There are many different data structures composing the Scala standard library. One of them is the : immutable List Compared to , modifying an index after having initialized a will lead to a compilation error. Array List Map A map can be initialized like this: Note the operator to associate a color key to its corresponding hexadecimal value. -> is an data structure. Adding an element means creating another : Map immutable Map Meanwhile, the elements cannot be modified. In the case we need a mutable structure, we can use : scala.collection.mutable.Map In this example, we mutated the key. AK Methods/Functions: Basics We have to make the distinction between methods and functions. A method is a function that is a of a class, trait or object (we are going those notions). member Let’s see a basic method example: Here we defined an method with the keyword. It took two as an input and returned an . Both inputs are (in the sense that they are managed just like if they were declared as ). add def Int Int immutable val The keyword is optional. The method will automatically return the last expression. Moreover, it’s worth mentioning that in Scala (compared to Java), exits the , not the current block. return return current method One last thing to add, the . The Scala compiler is also able to infer it. Yet, for the sake of code maintainability, it might be a good option to set it explicitly. return type is optional Furthermore, a method without output can be written is both ways: We can also return multiple outputs like this: This prevents us from having to wrap a set of outputs in a specific object. Another syntactic sugar to mention. Let’s consider a method without arguments. We can call this method in both ways: bar The best practice is to keep the parenthesis only if introduces a side-effect. Otherwise, we call like in the second expression. bar bar Also, Scala allows us to indicate that a method argument can be . Just like in Java, this repeatable argument must be the last parameter: repeated Here, we iterate over each element and we return an aggregated sum. args Last but not least, we can also define default parameters value: Invoking without providing a value for can be done in two ways. default x First, using the operator: _ Or, using named arguments like this: Methods/Functions: Advanced Nested Methods In Scala, we can . Let’s consider the following example: nest method definitions In this case, method is used solely by . To restrict its access, we may decide to set it (we’ll see later on the different visibility levels). mergesort2 mergesort1 private Yet, in Scala we can also decide to nest the second method into the first one like that: becomes available only in the scope of . mergesort2 mergesort1 Higher-Order Functions Higher-order functions . As an example of a method taking a function as a parameter: take as parameters a function or return a function as a result is a function taking an as an input and returning an . In our example, delegates the execution to by passing to it. f Int Int foo f i Function Literals Scala is considered as a functional language in the sense that . It means we can express a function in a function literal syntax like that: every function is a value is a function with an type (which could have been inferred by the Scala compiler). For each integer it returns . increment Int => Int x x + 1 If we take again the previous example, we could pass to : increment foo We can also manage so-called anonymous functions: The second parameter is a function without any name. Closure A closure in a function literal which on the value of one or more variable/value declared outside this function. depends A simple example: Here, depends on which is declared outside of . foo Pi foo Partial Functions Let’s consider the following method to compute the speed from a distance and a time: Scala allows us to by calling it only with a subset of the mandatory inputs: partially apply speed Note that in this example, none of the parameters of have a default value. So in order to call it, we need to fill all the parameters. speed In this example, is a function of type . partialSpeed Float => Float Then, in the same way as we were calling , we can call like this: increment partialSpeed Currying A method can define multiple parameter lists: This method is doing exactly the same job than: Yet, the way to call is different: multiply Like requested by the method signature, we call it with two lists of parameters. Then, what if we call with only one list of parameters? multiply In this case, we partially applied which gives us in return an function. multiply Int => Int What are the ? Let’s consider a function to send a message given a particular context: benefits As you can see, there’s an effort made to make this function pure. Instead of depending on an external context, we make it available as a parameter of the function. send Yet, it might be somewhat tedious to have to pass this context during every single call of . Or maybe a function does not need to know about the context. send One solution may be to partially apply with a predefined context and to manage an function. send Array[Byte] => Unit Another solution is to curry and make the parameter like this: send context implicit How can we call in this case? We can define an context before to call : send implicit send The keyword means that for every function managing an implicit parameter, we don’t even need to pass it. It will be mapped by the Scala compiler. implicit Context automatically In our case, manages the object as a potential implicit (we can also decide to pass it explicitly). So, we can simply call with the first argument list. send Context send Classes A class in Scala is a similar concept than in Java: exposed a default constructor because of the syntax line 1. Meanwhile, and are two members of the class. Point (Int, Int) x y A class can also contain a collection of methods just like in the previous example. move We can instantiate with the keyword: Point new A class can be abstract meaning it cannot be instantiated. Case Classes Case classes are a particular kind of classes. If you are familiar with DDD (Domain Driven Design), a case class is a . value object By default, a case class is : immutable The value of and cannot be changed. x y A case class must be instantiated without : new Case classes (compared to regular classes) are compared by value (and not by reference): Objects An object in Scala is a : singleton Objects are defined with the keyword (👏). object Traits Traits are in a way similar to Java interfaces. They are used to . As an example: share interfaces between classes but also fields A trait method can also have a default implementation. Traits cannot be instantiated but they can be extended by classes and objects. Visibility In Scala, every member of a class/object/trait is public by default. There are two other access modifiers: : members are only accessible from sub-classes protected : members are only accessible from the current class/object private Furthermore, we can also have a more granular way to restrict access by specifying a package in which the restriction is applied. Let’s consider a class in a package. If we want to make a method private only outside of we can do it this way: Foo bar bar Generics Generics is also a feature provided by Scala: To instantiate a generic class: If-else If-else syntax is similar in Scala than in several other languages: Yet, in Scala an if-else statement is also an . It means we can, for example, define methods like this: expression Loops A basic loop can be implemented like this: means from 0 to 10 included whereas means from 0 to 10 excluded. to until We can also loop over two elements: In this example we iterated over all the possible tuple combinations: a=0, b=0a=0, b=1a=0, b=2a=1, b=0a=1, b=1a=1, b=2 We can also include conditions in the for. Let’s consider the following list of elements: If we need to iterate over each element of and consider only the even integers, we can do it this way: list Moreover, Scala provides so-called to create sequence of elements with the form . As an example: for comprehensions for() yield element In this example, we created a collection of even integers by iterating over each element and it in case it is even. As a result, will be inferred as a sequence of integers (a object, the parent of ). yielding sub Seq List In the same way than with if-else statement, for is also an expression. So we can also define methods like this: Pattern Matching Pattern matching is a mechanism to check a value against a given pattern. It is an enhanced version of the Java statement. switch Let’s consider a simple function to translate an integer into a string: Scala adds a bit of syntactic sugar to implement an equivalent this way: First, we removed the statements. Then, function becomes a pattern matcher as we removed the block statement after the function definition. return matchB Anything else apart from some sugar? Pattern matching is a great addition to case classes. Let’s consider an example taken from . Scala documentation We want to return a depending on a notification type. We define an abstract class and two case classes and : String Notification Email SMS The most elegant way to do it in Scala is to use on the notification: pattern matching This mechanism allows us to the given and to automatically parse the parameters we are interested in. For example, in the case of an email, maybe we are not interested in displaying the so we simply omit it with keyword. cast notification body _ Exceptions Let’s consider the concrete use case where we need to print the number of bytes from a given file. To perform the I/O operation, we are going to use which may throw exceptions. java.io.FileReader The most common way to do it if you are a Java developer would be something like this using a try/catch statement: The second way to implement it is somewhat similar to Java . As a reminder, is a container brought in Java 8 for optional values. Optional Optional In Scala, is a . It is an abstract class, extended by two case classes and . Try container for success or failures Success Failure We first wrap the creation of a new in a call. We use a map to convert an eventual to an by calling the method. As a result, we get a . FileReader Try FileReader Int read Try[Int] Then, we can use pattern matching to determine the type of . tried Implicit Conversions Let’s analyze the following example: We defined two case classes and . Foo Bar Meanwhile, an object exposes a method taking a in parameter. Consumer consume Foo In , we call but as required by the signature of the method but with a . How is this possible? Test Consumer.consume() not with a Foo Bar In Scala, we can define implicit conversions between two classes. In the last example, we simply need to describe how to convert a to a : Bar Foo If this method is imported, the Scala compiler will make sure that we can call with a or a . barToFoo consumer either Foo Bar Concurrency To handle concurrency, Scala was initially based on the . Scala was providing the library. Yet, since Scala 2.10 this library became deprecated in favor of . actor model scala.actors Akka actors Akka is a set of libraries for implementing concurrent and distributed applications. Nonetheless, we can also use Akka only at the scale of a single process. The main idea is to manage actors as a . An actor can send messages to other actors, receive and react to messages and spawn new actors. primitive for concurrent computation Example of communications within an actor system Just like other concurrent computation models like CSP (Communicating Sequential Processes), the key is to communicate instead of sharing memory between different threads. through messages Credits: baloocartoons.com Scala is a very elegant language. Yet, the learning curve is not that small compared to other languages like Go for example. Reading an existing Scala code as a beginner might be somewhat difficult. But once you start to master it, developing an application can be done in a very efficient way. Further Reading _Install Scala on your computer and start writing some Scala code!_docs.scala-lang.org Documentation | Scala Documentation
Share Your Thoughts