- Scala cheat sheet from coursera
- Scala reactive cheat sheet
- Scala By Example
- Scala cheat sheet
- Scala School
- Scalacheck
- Tour of scala
- Learning Scalaz
- Call by value - evaluates the function arguments before calling the function
- Call by name - evaluates the function first, and then evaluates the arguments if need be
def square(x: Double) // call by value
def square(x: => Double) // call by name
Converting a function with multiple arguments into a function with a single argument that returns another function.
def f(a: Int, b: Int): Int = a + b // uncurried version (type is (Int, Int) => Int)
def f(a: Int)(b: Int): Int = a + b // curried version (type is Int => Int => Int)
def inc = f(1)_ // (type is Int => Int)
The precedence of an operator is determined by its first character, with the following in decreasing order of precedence:
(all other special characters)
*/ %
+-
:
=!
<>
&
ˆ
|
(all letters)
(all assignment operators)
((a + b) ^? (c ?^ d)) less ((a ==> b) | c)
The associativity of an operator is determined by its last character
- Right-associative if ending with
:
- Left-associative otherwise
1 + 2 // Scala invokes (1).+(2)
1 :: 2 :: Nil // Scala invokes (Nil).::(2).::(1)
Notation
- infix
- prefix (olny with
+
,-
,!
, and~
) - postfix
1 + 2 // infix
-1 // prefix - Scala invokes (1).unary_-
1 toLong // postfix
Principal forms of polymorphism:
- subtyping
- generics
- upper bound -
S <: T
means:S
is a subtype ofT
(S
narrowsT
,T
is an upper bound ofS
) - lower bound -
S >: T
means:S
is a supertype ofT
(S
widensT
,T
is a lower bound ofS
) - mixed bound -
S >: T <: U
means:S
is a supertype ofT
and a subtype ofU
(S
widensT
and narrowsU
)
class Foo[A,B] {
def fun1[C <: A](arg: C): C = arg
def fun2[C >: B](arg: C): C = arg
def fun3[C >: B <: A](arg: C): C = arg
}
val foo = new Foo[Seq[Int], List[Int]]
foo.fun1(1 to 5) // Range is a subtype of Seq
foo.fun2(Iterable(1, 2, 3)) // Iterable is a supertype of List
foo.fun3(List(1,2,3)) // List is a subtype od Seq and a supertype of List
// foo.fun3(Iterable(1, 2, 3)) // Won't compile because Iterable is not a subtype
// of Seq (Seq is a subtype of Iterable)
Given A <: B
- If
C[A] <: C[B]
,C
is covariant - If
C[A] >: C[B]
,C
is contravariant - Otherwise
C
is nonvariant
class C[+A] { ... } // C is covariant
class C[-A] { ... } // C is contravariant
class C[A] { ... } // C is nonvariant
For a function, if A2 <: A1
and B1 <: B2
, then A1 => B1 <: A2 => B2
def fun1(seq : Seq[String]) : List[Int] = seq map (_.toInt) toList
def fun2(list : List[String]) : Seq[Int] = list map (_.toInt) // fun1 <: fun2
var fun = fun2 _
var res : Seq[Int] = fun(List("1", "2"))
fun = fun1 _ // because List[String] <: Seq[String] and List[Int] <: Seq[Int] => fun1 <: fun2
res = fun(List("1", "2"))
Functions must be contravariant in their argument types and covariant in their result types, e.g.
trait Function1[-T, +U] {
def apply(x: T): U
} // Variance check is OK because T is contravariant and U is covariant
Pattern matching is used for decomposing data structures
selector match {
case pattern1 => expression1
...
case patternM => expressionM
case _ => expressionN
}
Examlpe of pattern guard
expr match {
case x : Int if x >= 0 => print(s"$x is positive or zero")
case x : Int if x < 0 => print(s"$x is negative")
}
The wildcard pattern _
matches any object
expr match {
case (_, _) => print(s"$expr is a pair")
case _ =>
}
A constant pattern matches only itself
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
describe(5) // yields "five"
describe("5") // yields "something else"
describe("hello") // yields "hi!"
describe(Nil) // yields "the empty list"
A variable pattern matches any object, just like a wildcard. Moreover Scala binds the variable to whatever the object is.
expr match {
case 0 => "zero"
case somethingElse => s"not zero: $somethingElse"
}
Variable or constant?
- Variable pattern - name starting with a lowercase letter
- Constant pattern - all other
import math.Pi
expr match {
case 0 => "zero"
case Pi => "Pi"
case somethingElse => s"not zero an pi: $somethingElse"
}
case class Email(name : String, domain : String)
expr match {
case Email(name, domain) => println(s"$name@$domain")
case _ =>
}
List(1, 2, 3) match {
case Nil => ...
case x :: Nil => ... // or case List(x) => ...
case x :: xs => ...
}
::
implemented using case class scala.collection.immutable.::
assert(scala.collection.immutable.::(1, Nil) == 1 :: Nil)
object EMail {
// The injection method (optional)
def apply(user: String, domain: String) = user +"@"+ domain
// The extraction method (mandatory)
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
and
val EMail(name, domain) = "[email protected]" // name = slawomir.sledz, doamin = gmail.com
Only with extraction method
object UpperCase {
def unapply(s: String): Boolean = s.toUpperCase == s
}
expr match {
case a @ UpperCase() => print("UpperCase")
case _ => print("Not UpperCase")
}
object Domain {
// The injection method (optional)
def apply(parts: String*): String = parts.reverse.mkString(".")
// The extraction method (mandatory)
def unapplySeq(whole: String): Option[Seq[String]] = Some(whole.split("\\.").reverse)
}
val Domain("pl", c, _*) = "a.b.c.pl" // c = c
val Domain("pl", c, b, _*) = "a.b.c.pl" // b = b, c = c
expr match {
case List(0, _, _) => println("found it")
case _ =>
}
Match against a sequence without specifying how long it can be (_*
- matches any number of elements including zero elements)
expr match {
case List(0, _*) => println("found it")
case _ =>
}
expr match {
case (a, b, c) => println("matched "+ a + b + c)
case _ =>
}
Convenient replacement for type tests and type casts.
x match {
case s: String => s.length
case m: Map[_, _] => m.size
case _ => -1
Via the @
sign. Set the variable e
to the matched object.
abstract class Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
val expr = UnOp("-", UnOp("-", Number(1)))
val expr2 = expr match {
case UnOp("-", e @ UnOp("-", _)) => e
case _ =>
}
assert(expr2 == UnOp("-", Number(1)))
A monad is a parametric type M[T]
with two operations:
- flatMap
- unit
trait M[T] {
def flatMap[U](f: T => M[U]) : M[U]
def unit[T](x: T) : M[T]
}
These operations must satisfy three important properties:
- Associativity:
(x flatMap f) flatMap g == x flatMap (y => f(y) flatMap g)
- Left unit:
unit(x) flatMap f == f(x)
- Right unit:
m flatMap unit == m
trait Generator[+T] { self =>
def generate: T
def map[S](f: T => S) : Generator[S] = new Generator[S] {
def generate = f(self.generate)
}
def flatMap[S](f: T => Generator[S]) : Generator[S] = new Generator[S] {
def generate = f(self.generate).generate
}
}
Basic integer random generator
val integers = new Generator[Int] {
val rand = new java.util.Random
def generate = rand.nextInt()
}
Above definition can be mapped to the other domains to get booleans, pairs intervals using for-expression magic
val booleans = for {x <- integers} yield x > 0
val pairs = for {x <- integers; y<- integers} yield (x, y)
def interval(lo: Int, hi: Int) : Generator[Int] = for { X <- integers } yield lo + x % (hi - lo)
trait PartialFunction[-A, +R] extends Function1[-A, +R] {
def apply(x: A): R
def isDefinedAt(x: A): Boolean
}
val pf: PartialFunction[Int, String] = {
case 1 => "one"
case 2 => "two"
// no case for other numbers => pf is undefined for other than 1 and 2
}
print(pf.isDefinedAt(1)) // true
print(pf(1)) // one
print(pf.isDefinedAt(3)) // false
Cases:
- conversions to an expected type
- conversions of the receiverof a selection
- implicit parameters
Rules for implicits
- Marking Rule: Only definitions marked implicit are available
- Scope Rule: An inserted implicit conversion must be in scope
- companion object of the source or target of the conversion
- import it as a single identifier
- One-at-a-time Rule: Only one implicit is tried
- Explicits-First Rule: Whenever code type checks as it is written, no implicits are attempted
One implicit conversion is more specific than another if
- The argument type of the first one is a subtype of the second.
- Both conversions are methods, and the enclosing class of the first one extends the enclosing class of the second
Conversion from functions to action listeners
implicit def function2ActionListener(f: ActionEvent => Unit) = new ActionListener {
def actionPerformed(event: ActionEvent) = f(event)
}
And thanks to the above, below compiles (if conversion is in scope)
button.addActionListener( (_: ActionEvent) => println("pressed!") )
Let's say we have
class Rational(n: Int, d: Int) {
...
def + (that: Rational): Rational = ...
def + (that: Int): Rational = ...
}
val oneHalf = new Rational(1, 2) // 1/2
oneHalf + oneHalf // 1/1
oneHalf + 1 // 1
and we want also to support
1 + oneHalf
to do this add below to the scope
implicit def intToRational(x: Int) = new Rational(x, 1)
def maxList[T](elements: List[T])(implicit converter: T => Ordered[T]): T =
elements match {
case List() =>
throw new IllegalArgumentException("empty list!")
case List(x) => x
case x :: rest =>
val maxRest = maxList(rest) // (orderer) is implicit
if (x > maxRest) x // orderer(x) is implicit
else maxRest
}
Above method header can be rewritten to use view bound
def maxList[T < % Ordered[T]](elements: List[T]): T = ...
T < % Ordered[T]
- I can use any T , so long as T can be treated as an Ordered[T].
Example
for (x <- 1 to M; y <- 1 to N) yield (x,y)
is equivalent to
(1 to M) flatMap (x => (1 to N) map (y => (x, y)))