7. High level features
●
Anonymous functions
●
Lambda expressions
●
Higher order functions
●
Functions as arguments and values
●
Annotations
●
Run-time reflection
11. Basic Syntax
●
Everything is an expression
●
Almost identical expression & block syntax
●
Semicolons not needed
●
Block evaluates to its last expression
●
Type inference
●
Type annotations can be often omitted
●
Type information is deduced by the compiler
●
Automated code translation
●
http://javatoscala.com/
13. Class and field syntax
●
Java
public class ScalaSyntax implements Quite, Simple {
public Double mutable;
public final String something = "No changes";
public ScalaSyntax(final Double mutable) {
this.mutable = mutable;
System.out.println("New instance");
}
}
●
Scala
class ScalaSyntax(var mutable: String) extends Quite with Simple {
println("New instance")
val something = "No changes"
}
14. Type parameter syntax
●
Java
public <T> T generic(Iterator<T> items) {
if (items.hasNext()) {
return items.next();
} else {
return items.next();
}
}
●
Scala
def generic[T](items: Iterator[T]): T = {
if (items.hasNext) {
items.next()
} else {
items.next()
}
}
15. Control flow
●
No checked exceptions
●
No switch statement
●
Pattern matching
●
No operators
●
Methods with symbolic names and infix notation
// infix notation
"Concatenate" + "Strings"
// equivalent dot notation
"Concatenate".+("Strings")
def !#%(symbolic: String): String = symbolic + "Method"
16. No primitive types
●
Equivalent types
●
int Int
→
●
long Long
→
●
byte Byte
→
●
short Short
→
●
double Double
→
●
float Float
→
●
Optimization via value classes
●
extends AnyVal
17. No classic for statement
●
Behaves like enhanced for statement
●
Java
for (int index = 0; index < 7; index++);
for (char character : "text".toCharArray()) {
System.out.println(character);
}
●
Scala
for (index <- 0 until 7) ()
for (character <- "text") {
println(character)
}
18. No static members
●
Lazily initialized singleton object instead
●
Companion object
●
Java
public class Data {
public String variable = "";
public static final String constant = "value";
}
●
Scala
class Data {
var variable: String = ""
}
object Data {
val constant = "value"
}
19. Constructor constraints
●
Constructor must call another constructor first
class ConstructorConstraints(val size: Int) {
def this(size: Int, multiplier: Int) = {
this(size * multiplier)
}
def this() {
// workaround
this(Helper.generateSize(), 1)
}
}
object Helper {
def generateSize(): Int = 0
}
21. Calling Java code
●
Libraries in the classpath
●
Scala and Java sources in the same project
●
src/main/scala/*.scala
●
src/main/java/*.java
"test".toString
"test".toString()
val result = new java.util.ArrayList[Int]()
class SomeResource extends AutoCloseable {
override def close(): Unit = ()
}
22. Called by Java code
●
Declaring checked exceptions
●
Scala
class CalledByJava {
var property = "mutator method generated"
// declare checked exception
@throws(classOf[IOException])
def send(something: String): Unit = { }
}
●
Java
CalledByJava test = new CalledByJava();
test.property();
test.property_$eq("mutator method called");
// catch checked exception
try { test.send("error"); } catch (IOException e) { }
23. Converting collections
import java.util
import scala.collection.JavaConverters._
// Java collection
val javaMap = new util.HashMap[String, Int]()
// Java -> Scala
val scalaMap = javaMap.asScala
scalaMap.mkString(", ")
// Scala -> Java
val javaMapAgain: util.Map[String, Int] = scalaMap.asJava
25. String interpolation
●
Programmable via custom interpolators
val neat = 7
// standard library
val substitution = s"Is $neat"
val printf = f"Number is $neat%02d"
val noEscaping = raw"somenthing"
// external library
val document = json"""{ "hello": "world" }"""
26. Detour to Python
●
Named arguments
●
Default arguments
●
Tuples
def very(standard: String, default: String = "value"): Unit = ()
very("text")
def handy(default: String = "value", standard: String): Unit = ()
handy(standard = "text")
def firstOf(tuple: (Int, Double)): Int = tuple._1
val tuple = (1, 2.3)
firstOf(tuple)
27. Flexible scoping
●
Fields in traits
●
Abstract members
●
Nested function definitions
●
Multiple classes & objects in one source file
trait FlexibleScoping {
def abstractMethod(text: String): String
val abstractField: Int
def get: String = {
def compute: String = "result"
abstractMethod(compute)
}
}
28. Pattern matching
●
Object decomposition
●
Companion object with unapply() method
def describe(value: Any): String = value match {
case 1 => "One"
case x: Int => s"Integer $x"
case x: Double if x < 1.2 => s"Small double $x"
case _:Float | _:Double => "Floating point"
case _ => "Anything else"
}
// one = 1, two = 2.3
val (one, two) = (1, 2.3)
// first = 1, third = 3, head = 1
val Vector(first, _, third) = Vector(1, 2, 3)
// head = 1, tail = List(2, 3)
val List(head, tail @ _*) = List(1, 2, 3)
29. Case classes
●
Equality, creation and decomposition is provided
●
Generated equals(), toString(), apply() and unapply() methods
●
Cannot be inherited from
●
Useful for algebraic data types
// algebraic data type
sealed abstract class Tree
case class Leaf(value: Int) extends Tree
case class Node(left: Tree, right: Tree) extends Tree
// evaluates to true
Node(Leaf(1), Leaf(2)) == Node(Leaf(1), Leaf(2))
val tree = Node(Leaf(1), Node(Leaf(2), Leaf(3)))
// value = 1
val Node(Leaf(value), _) = tree
30. For comprehensions
●
Syntactic construct for monadic function composition
●
Generates nested invocation of map() and flatMap()
val items = Vector(1, 2, 3)
val pairs = Map("x" -> 7, "y" -> 2)
// evaluates to Vector(2, 3, 4)
for (item <- items) yield item + 1
items.map(item => item + 1)
// evaluates to Vector((1, "x"), (1, "y"), (2, "x"), (2, "y"))
for {
item <- items if item < 3
(key, value) <- pairs
} yield { (item, key, value) }
items.filter(item => item < 3).flatMap(item => pairs.map {
case (key, value) => (item, key)
})
31. Implicits
●
Automated passing & conversion of method arguments
●
Searches in current scope, imports, companion objects, etc.
def more(value: Int)(implicit offset: Int): Int = 2 * value + offset
implicit val offset = 1
// implicit value passed as an argument
more(7)
more(7)(offset)
implicit def convert(value: Vector[_]): Int = value.size
// an argument converted using implicit method
more(Vector(1, 2, 3))
more(convert(Vector(1, 2, 3)))(offset)
32. Lazy evaluation
●
Evaluation is eager by default
●
Call-by-name evaluation syntax
●
Lazy evaluation support
def callByValue(input: Int): Int = input + 1
def callByName(input: => Int): Int = {
// input is evaluated here every time
input + 1
}
def callByNeed(input: => Int): Int = {
// input is evaluated here but only once
lazy val memoized = input
memoized
}
33. Other interesting features
●
Partial function invocation
●
Currying
●
Partially applied functions
●
Type classes
●
Enrich functionality of existing type without extending it
●
Dynamic type checking
●
Member lookup via applyDynamic()
●
Macros
●
Compile-time reflection & code generation
34. Type system overview
●
Compound types
●
Types intersection in type annotation
●
Mixin class composition
●
Multiple inheritance with traits
●
Type aliases
●
Abstract type members
●
Upper & lower type bounds
●
Type variance
36. Type system overview
●
Type lambdas
●
F-bounded types
●
Self-recursive types
●
Higher-order types
●
Type constructor kinds
●
Value classes
●
Type specialization