Scala has two kinds of variables, vals and vars:
- val is immutable, cannot be reassigned
- var is mutable, can be reassigned
Scala as singleton objects, looks like class definition but uses keyword object
.
When a singleton object shares the same name with a class, it is called that class's companion object.
Traits are similar to Java interfaces. They are used to define object types by specifying the 'signature' of the supported methods. A class can mix any number of traits (but only extend a single class).
In Scala, can use a type bound to limit the type of classes that can be parameterized. More information in Scala documentation
class Cage[P <: Pet](p: P) {
def pet: P = p
}
object Main extends App {
var dogCage = new Cage[Dog](new Dog)
var catCage = new Cage[Cat](new Cat)
/* Cannot put Lion in a cage as Lion is not a Pet. */
// var lionCage = new Cage[Lion](new Lion)
Looks like message is passed as a parameter, but it can be used in the SayHi() method. It is actually a field in the class.
class Greeter(message: String) {
println("I am the primary constructor and the object is being instantiated")
def SayHi() = println(message)
}
val greeter = new Greeter()
greeter.SayHi()
Apply serves the purpose of closing the gap between OOP and FP paradigms. Each function in Scala can be represented by an object. Every object can also be treated as a function, provided it has an apply function. Also used for auxiliary constructors.
See http://blog.edmondcote.com/2017/05/use-of-hellaqueue-in-chisel.html for more information.
def max(x: Int, y: Init): Int = {
^^ return type
if (x < y)
x
^^ return val
else
y
^^ return val
}
args.foreach(arg => println(arg))
// ^^ passing a function literal
args.foreach((arg: String) => println(arg))
// ^^ strict type inference
args.foreach(println)
// ^^ if functon literal has one statement and single argument
// use partially applied function shorthand
Functions are first class in Scala
() => println("I am an anonymous function!")
Here is a more elaborate example.
val concat_fruit = (x: String, y: String) => x + y // note, this is val not def
def apply_to_args(func: (String, String) => String, arg1: String, arg2: String): String = func(arg1,arg2) // pass anonymous function
assert(apply_to_args(concat_fruit, "apple", "orange") == concat_fruit("apple", "orange"))
Scala uses _ to act as a placeholder for parameters in the anonymous function.
List(1,2,3,4,5).foreach(print(_)) // is equivalent to ..
List(1,2,3,4,5).foreach( a => print(a))
val sum = List(1,2,3,4,5).reduceLeft(_+_) // is equivalent to ..
val sum = List(1,2,3,4,5).reduceLeft((a,b) => a + b)
The type passed is substituted for the value inside the function. For example:
def f(x: => Int) = x * x
var y = 0
f { y +=1 ; y } // will execute like { y += 1; y } * { y += 1; y }
The following implements what an append operation using currying:
def concat_curried(fruit: String)(veg: String): String = fruit + veg
val curried = concat_curried("apple")_
assert(curried("spinach") == "applespinach")
Execute 'x + 1' on each element of list, return list.
val foo = Array(1,2,3,4,5)
val bar = for (x <- foo) yield x + 1
assert(bar.deep == Array(2,3,4,5,6).deep)
Explicit zip method, demo example:
val arr1 = Array(1,2,3)
val arr2 = Array(4,5,6)
assert(arr1.zip(arr2).deep == Array((1,4),(2,5),(3,6)).deep)
Zip with index, demo example:
assert(Array("foo","bar","baz").zipWithIndex.deep == Array(("foo",0),("bar",1),("baz",2)).deep)
val res = for ((y,x) <- Array("foo","bar","baz").zipWithIndex) yield (x,y) // reverse order
assert(res.deep == Array((0,"foo"),(1,"bar"),(2,"baz")).deep)
For val b, we are passing a function to map.
val a = for (c <- Array(1,2,3)) yield c + 2
val b = Array(1,2,3).map(_ + 2) // not Map
assert (a.deep == b.deep)
A simple way to run an algorithm concurrently. It is said that a future returns "eventually". Here is a simplified example, from this site.
object Futures1 extends App {
// 2 - create a Future
val f = Future {
sleep(500) // def sleep(time: Long) { Thread.sleep(time) }
1 + 1
}
// 3 - this is blocking (blocking is bad)
val result = Await.result(f, 1 second)
println(result)
sleep(1000)
}
Tutorial explains that the better approach to working with futures is to use a callback function. There are three callback methods: onComplete, onSuccess, and onFailure. In the example below, f.onComplete does not block.
object Example1 extends App {
println("starting calculation ...")
val f = Future {
sleep(Random.nextInt(500))
42
}
println("before onComplete")
f.onComplete {
case Success(value) => println(s"Got the callback, meaning = $value")
case Failure(e) => e.printStackTrace
}
// do the rest of your work
println("A ..."); sleep(100)
println("B ..."); sleep(100)
// [..]
sleep(2000) // important to keep JVM from shutting down
}
A function can return a future.
object Cloud {
def runAlgorithm(i: Int): Future[Int] = future {
sleep(Random.nextInt(500))
val result = i + 10
println(s"returning result from cloud: $result")
result
}
}
You can run this algorithm in parallel and join once complete.
println("starting futures")
val result1 = Cloud.runAlgorithm(10)
val result2 = Cloud.runAlgorithm(20)
val result3 = Cloud.runAlgorithm(30)
println("before for-comprehension")
val result = for {
r1 <- result1
r2 <- result2
r3 <- result3
} yield (r1 + r2 + r3)
println("before onSuccess")
result onSuccess {
case result => println(s"total = $result")
}
More information in this tutorial
Pattern matching is similar to switch statement, but is more powerful.
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "anything other than 1 or 2"
}
_ acts like a wildcard, it will match anything.
- This code must be in separate object. Outside of compilation scope of where you want to use it.
- Comment out uses of macro when changing the macro. Requires (I think) a two step compilation process.
package tests
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
object EngineTestMacros {
/** Defines a ScalaTest using macro. */
def defineTest(s: Any): Unit = macro defineTestMacro
/** Implementation of defineTest. */
def defineTestMacro(c: Context)(s: c.Expr[Any]): c.Expr[Unit] = {
import c.universe._
c.Expr(
q"""
val test: EngineTest = $s
"Test '" + test.name + "'" should "not trigger assetions and pass golden check" in {
// run test here
}
Unit
""")
}
}