Introduction
Resources
- Programming in Scala (2016)(3rd) by Martin Odersky, Lex Spoon, and Bill Venners (Book)
- Scala High Performance Programming (2016) by Vincent Theron, Michael Diamant (Book)
- Scala Puzzlers
- Tour of Scala (Documentation)
- Scala Improvement Proposals
- Twitter Scala School
- S-99: Ninety-Nine Scala Problems
- Scala Exercises
- Scala for Project Euler
- Scala Collections performance characteristics
- The Neophyte's Guide to Scala
- Scala Compiler Phases with Pictures
- Most Useful Resources For Learning Scala
Related projects
- sbt - The interactive build tool
- Dotty - A next-generation compiler for Scala
- Scaladex - The Scala Library Index
- ScalaFiddle - An online playground for creating, sharing and embedding Scala fiddles
- Scastie - Scastie is an interactive playground for Scala with support for sbt configuration
- Ammonite - Scala Scripting
- Scala Native - An optimizing ahead-of-time compiler and lightweight managed runtime
- Scala.js - Optimizes Scala code into highly efficient JavaScript
FAQ
What is the Scala hierarchy?
What is the difference between by-name and by-value parameters?
A by-value parameter is evaluated before the method is invoked e.g. (x: Int)
while a by-name parameter is not evaluated before the method is invoked, but each time the parameter is referenced inside the method e.g. (x: => Int)
What is the difference between eager, lazy and memoized evaluation?
- Eager computations happen immediately
- Lazy computations happen on access
- Memoized computations are run once on first access, after which the results are cached
def val lazy var
?
What are the differences between def
defines a method, it is lazy and not memoizedval
defines a fixed value, it is immutable, eagerly initialized and memoizedlazy val
is only initialised when required and as late as possible (deferred evaluation), it is not recomputed like by-name parameters i.e. it's lazy and memoizedvar
defines a variable reference, it is mutable and should be avoided
Nothing Nil None Empty Null null Unit
?
What are Nothing
is a trait that is the bottom subtype of every subtype ofAny
Nil
is an empty list that is defined as aList[Nothing]
None
is an empty option that is defined as aOption[Nothing]
Null
is a trait and is the bottom type similar toNothing
but only forAnyRef
notAnyVal
null
is an instance of theNull
traitUnit
is a subtype ofAnyVal
, it's only value is()
and it is not represented by any object in the underlying runtime system. A method with return typeUnit
is analogous to a Java method which is declaredvoid
What good are right-associative methods?
All methods whose names end in :
are right-associative. That is, the expression x :: xs
is actually the method call xs.::(x)
, which in turn calls the data constructor ::(x, xs)
What is type inference?
- Type inference (Documentation)
The compiler can often infer the type of an expression so you don't have to declare it explicitly
val myInt = 8
// myInt: Int = 8
val myString = "hello"
// myString: String = "hello"
What is a companion object?
- Singleton Objects (Documentation)
- How to override apply in a case class companion?
An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val. An object with the same name as a class is called a companion object. A companion class or object can access the private members of its companion
static
members in Java are modeled as ordinary members of a companion object in Scala
What does it mean reify a trait?
You can't call functions on a trait, so you need to create a concrete instance of that trait before you do anything else. This technique is common with the modular programming approach, and it's known as reifying the trait. The word reify is defined as "Taking an abstract concept and making it concrete"
trait MyTrait {
def myMethod = "hello"
}
object MyTrait extends MyTrait
MyTrait.myMethod
// res0: String = "hello"
What is the relationship between currying and partially applied function?
- Currying (Documentation)
- Partially-Applied Functions (and Currying) in Scala
- How to use partially applied functions in Scala
Currying is a means of transforming a function that takes more than one argument into a chain of calls to functions, each of which takes a single argument
When you call a function that has parameters, you are said to be applying the function to the parameters. When all the parameters are passed to the function, you have fully applied the function to all of the parameters. But when you give only a subset of the parameters to the function, the result of the expression is a partially applied function
When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing parameter lists as its arguments
val sum = (a: Int, b: Int, c: Int) => a + b + c
// sum: (Int, Int, Int) => Int = <function3>
// fully applied function
sum(1, 2, 3)
// res1: Int = 6
// partially applied function
val f = sum(1, 2, _: Int)
// f: Int => Int = <function1>
f(3)
// res2: Int = 6
def curry[A, B, C](f: (A, B) => C): A => (B => C) = ???
def uncurry[A, B, C](f: A => B => C): (A, B) => C = ???
What is a variadic function?
A variadic function accepts zero or more arguments. It provides a little syntactic sugar for creating and passing a Seq of elements explicitly. The special _*
type annotation allows to pass a Seq to a variadic method
sealed trait MyList[+A]
case object MyNil extends MyList[Nothing]
case class MyCons[+A](head: A, tail: MyList[A]) extends MyList[A]
object MyList {
def apply[A](list: A*): MyList[A] =
if (list.isEmpty) MyNil
else MyCons(list.head, apply(list.tail: _*))
}
MyList()
// res3: MyList[Nothing] = MyNil
MyList("a", "b")
// res4: MyList[String] = MyCons(
// head = "a",
// tail = MyCons(head = "b", tail = MyNil)
// )
MyList(1, 2, 3)
// res5: MyList[Int] = MyCons(
// head = 1,
// tail = MyCons(head = 2, tail = MyCons(head = 3, tail = MyNil))
// )
What is type ascription?
- Ascription (Documentation)
Ascription is basically just an up-cast performed at compile-time for the sake of the type checker
What is covariance and contravariance?
- Covariance and contravariance in Scala
- The Scala Type System: Parameterized Types and Variances
- Cheat Codes for Contravariance and Covariance
Variance relates to subtypes i.e. B
is a subtype of A
if we can use a value of type B
anywhere we expect a value of type A
. Covariance and contravariance annotations arise when working with type constructors
Covariance means that the type F[B]
is a subtype of the type F[A]
if B
is a subtype of A
trait F[+A]
// example
trait List[+A]
trait Option[+A]
Contravariance means that the type F[B]
is a subtype of F[A]
if A
is a subtype of B
trait F[-A]
Invariance means the types F[A]
and F[B]
are never subtypes of one another, no matter what the relationship between A
and B
. This is the default semantics for Scala type constructors
trait F[A]
What is autoboxing?
The JVM defines primitive types (boolean
, byte
, char
, float
, int
, long
, short
and double
) that are stack-allocated rather than heap-allocated. When a generic type is introduced, for example, scala.collection.immutable.List
, the JVM references an object equivalent, instead of a primitive type. For example, an instantiated list of integers would be heap-allocated objects rather than integer primitives.
The process of converting a primitive to its object equivalent is called boxing, and the reverse process is called unboxing. Boxing is a relevant concern for performance-sensitive programming because boxing involves heap allocation. In performance-sensitive code that performs numerical computations, the cost of boxing and unboxing can can create significant performance slowdowns
@specialized
annotation?
What is the
Specialization with @specialized
annotation, refers to the compile-time process of generating duplicate versions of a generic trait or class that refer directly to a primitive type instead of the associated object wrapper. At runtime, the compiler-generated version of the generic class (or, as it is commonly referred to, the specialized version of the class) is instantiated.
This process eliminates the runtime cost of boxing primitives, which means that you can define generic abstractions while retaining the performance of a handwritten, specialized implementation
@switch
annotation?
What is the In scenarios involving simple pattern match statements that directly match a value, using @switch
annotation provides a warning at compile time if the switch can't be compiled to a tableswitch or lookupswitch which procides better performance, because it results in a branch table rather than a decision tree
@inline
annotation?
What is the
An annotation on methods that requests that the compiler should try especially hard to inline the annotated method
Inlining a function means that instead of having a function call resulting in parameters being placed on the stack and an invoke operation occurring, the definition of the function is copied at compile time to where the invocation was made, saving the invocation overhead at runtime
To enable this feature you need to explicitly set the -optimize
compiler flag
What is a value class?
- Value classes (Documentation)
The AnyVal
class can be used to define a value class, which is optimized at compile time to avoid the allocation of an instance
final case class Price(value: BigDecimal) extends AnyVal {
def lowerThan(p: Price): Boolean = this.value < p.value
}
TODO What is a sealed trait?
TODO
What is the difference between foldLeft and foldRight?
foldLeft
traverses from left to right (start to finish)foldLeft
definitiondef foldLeft[B](z: B)(f: (B, A) => B): B
List(1, 2, 3).foldLeft("nil")((accumulator, item) => s"($item operation $accumulator)")
// res6: String = "(3 operation (2 operation (1 operation nil)))"
List(1, 2, 3).foldLeft(List.empty[Int])((a, i) => i :: a)
// res7: List[Int] = List(3, 2, 1)
foldRight
traverses from right to left (finish to start)foldRight
definitiondef foldRight[B](z: B)(f: (A, B) => B): B
List(1, 2, 3).foldRight("nil")((item, accumulator) => s"($item operation $accumulator)")
// res8: String = "(1 operation (2 operation (3 operation nil)))"
List(1, 2, 3).foldRight(List.empty[Int])((i, a) => i :: a)
// res9: List[Int] = List(1, 2, 3)
foldLeft
andfoldRight
are equivalent if the binary operation is associative - seeMonoid