Essential Scala
Case Objects
Case class without constructor -> case object
case object Citizen {
def firstName = "John"
def lastName = "Doe"
def name = firstName + " " + lastName
}
internally it will create this:
class Citizen { /* ... */ }
object Citizen extends Citizen { /* ... */ }
Traits
When we mark a trait as sealed we must define all of its subtypes in the same file. If all the subtypes of a trait are known, seal the trait
scala> def missingCase(v: Visitor) =
v match {
case User(_, _, _) => "Got a user"
}
<console>:21: warning: match may not be exhaustive. It would fail on the following input: Anonymous(_, _)
v match {
^
missingCase: (v: Visitor)String
It will provide compile time errors, because it knows all the possibilities.
Recursive data
final case class Broken(broken: Broken)
this will never be possible to instantiate
sealed trait IntList
final case object End extends IntList
final case class Pair(head: Int, tail: IntList) extends IntList
// construct with this:
Pair(1, Pair(2, Pair(3, End)))
// same thing
val d = End()
val c = Pair(3, d)
val b = Pair(2, c)
val a = Pair(1, b)
Because we defined an End object it is possible to instantiate it.
Tail recursion
You may be concerned that recursive calls will consume excessive stack space. Scala can apply an optimisation, called tail recursion, to many recursive functions to stop them consuming stack space.
def method1: Int =
1
def tailCall: Int =
method1
This is a tail call because it immediately returns the value.
def notATailCall: Int =
method1 + 2
This instead needs to first execute method1 and then add something to it.
Scala only optimises tail calls where the caller calls itself
You can manually add @tailrec
to a method to optimize it.
Placeholder syntax
_+_ // expands to `(a,b)=>a+b`
foo(_) // expands to `(a) => foo(a)`
foo(_, b) // expands to `(a) => foo(a, b)`
_(foo) // expands to `(a) => a(foo)`
// and so on...
convert method calls to functions
object Sum {
def sum(x: Int, y: Int) = x + y
}
Sum.sum
// <console>:9: error: missing arguments for method sum in object Sum;
// follow this method with `_' if you want to treat it as a partially applied function // Sum.sum
// ^
(Sum.sum _)
// res: (Int, Int) => Int = <function2>
Collections
Ranges
10 until 1 by -1
Like a for loop in java
Type Class Instances
implicit val ordering = Ordering.fromLessThan[Int](_ < _) scala> List(2, 4, 3).sorted
// res: List[Int] = List(2, 3, 4)
List(1, 7 ,5).sorted
// res: List[Int] = List(1, 5, 7)
Ordering is implicitly passed to the sorted method.
Type Class Pattern
A type class is a trait with at least one type variable. The type variables specify the concrete types the type class instances are defined for. Methods in the trait usually use the type variables.
trait ExampleTypeClass[A] {
def doSomething(in: A): Foo
}
Implicit Parameter Lists
object HtmlUtil {
def htmlify[A](data: A)(implicit writer: HtmlWriter[A]): String = {
writer.write(data)
}
}
This takes the data to convert to HTML, as well as an implicit parameter of the writer to use.
HtmlUtil.htmlify(Person("John", "john@example.com"))(PersonWriter)
// res: String = <span>John <john@example.com></span>
If you define an implicit value it will find it:
implicit object ApproximationWriter extends HtmlWriter[Int] { def write(in: Int): String =
s"It's definitely less than ${((in / 10) + 1) * 10}" }
// result
HtmlUtil.htmlify(2)
// res: String = It's definitely less than 10
Even better:
object HtmlWriter {
def apply[A](implicit writer: HtmlWriter[A]): HtmlWriter[A] =
writer
}
// used like this:
// hidden `.apply`
HtmlWriter[Person].write(Person("Noel", "noel@example.org"))
Context bounds
def pageTemplate[A](body: A)(implicit writer: HtmlWriter[A]): String = {
val renderedBody = body.toHtml
s"<html><head>...</head><body>${renderedBody}</body></html>"
}
// needed to implicitly pass a writer, so we can call `.toHtml`
// This is context bound, it will expand to the previous syntax internally
def pageTemplate[A : HtmlWriter](body: A): String = {
val renderedBody = body.toHtml
s"<html><head>...</head><body>${renderedBody}</body></html>"
}
implicitly
case class Example(name: String)
implicit val implicitExample = Example("implicit")
implicitly[Example]
// res: Example = Example(implicit)
implicitly[Example] == implicitExample
// res: Boolean = true
The implicitly method takes no parameters but has a generic type parameters. It returns the implicit matching the given type, assuming there is no ambiguity.
Real world example:
ProductLens.productId.set(productId) andThen ProductLens.pricingTables.modify(_.sortBy(_.moq)(implicitly[Ordering[Long]].reverse))
We need to use implicitly because we want the reversed one.
das sortiert üsig pricing tables reversed.
Es bruucht e implicit implementation vom Ordering
trait für Long
implicit object Long extends LongOrdering
s LongOrdering wird im scope importiert
sortBy erwartet es implicit ordering als 2. parameter list
I dem spezifische mues es nume uf dä wäg spezifiziere, wöu du’s no reversed wotsch.
Süsch isch LongOrdering automatisch dür scala.Math im Scope
wenns nid wettisch reverse sortiere:
ProductLens.pricingTables.modify(_.sortBy(_.moq))
dr compiler gseht, dass dr 2. parameter für sortBy fehlt, also goht er im scope go luege obs nöime e implicit value het wo zum type passt (Ordering[Long]
)
wenners findet wird das implicitly a sortBy übergeh
jetz wei mer aber das stüüre, drum implicitly
und denn chasch es anders specifye
wenn das nid hettisch müesstisch dini eigeti implementation vo Ordering[Long] mache
Implicit conversions
class B {
def bar = "This is the best method ever!"
}
class A
implicit def aToB(in: A): B = new B()
new A().bar
// res: String = This is the best method ever!
an implicit conversion from A->B happens here.