This document provides an overview of Scala, covering object-oriented programming concepts, functional programming concepts, and how Scala combines both paradigms. It discusses key Scala features like type inference, anonymous classes, generics, and immutable collections. It provides examples of common programming tasks like filtering, mapping, reducing collections as well as examples of recursion and higher-order functions. The document demonstrates how to implement functional objects like an immutable Rational number class in both Java and Scala style.
5. JAVAPROSANDCONS
Pros
Simple language
Portable
Lot's of libraries and frameworks
Big community
Cons
Class based OOP is used for everything, even when
inappropriate, due to lack of support for anything else
Lot's of boilerplate code
Favors mutability
6. WHYTRYOTHER JVM
LANGUAGES?
Those languages tend to be more expressive - say
more, write less
Discover new ways of thinking about problems
Use the existing Java libraries/frameworks to fill in the
gaps
8. OVERVIEW
Created by Martin Odersky
Appeared around 2003
Statically typed
Type inference
Mixture of OOP and FP
Actor concurrency
Very good Java interop
Currently the most widely adopted alternate JVM
language
10. PERSON IN JAVA
import static java.lang.String.format;
public class Person {
public final String name;
public final int age;
public Person(final String name, final int age) {
this.name = name;
this.age = age;
}
public String greet() {
return format("Hello, my name is %s", name);
}
public String greet(final Person person) {
return format("Hello, %s, my name is %s", person.name, name);
}
@Override
public String toString() {
return format("name=%s, age=%d", name, age);
}
}
11. SAMEPERSON IN SCALA
class Person(val name: String, val age: Int) {
def greet = "Hello, my name is %s".format(name)
def greet(person: Person) =
"Hello, %s, my name is %s".format(person.name, name)
override def toString = "name=%s, age=%d".format(name, age)
}
12. JOE,MEETJOE
val joe = new Person("Joe", 35)
val age = joe.age
val olderJoe = new Person(joe.name, age + 1)
joe.greet(olderJoe)
joe greet olderJoe
13. TYPEINFERENCE
Scala code looks a bit like scripting language, right?
That is because Scala compiler can sometimes infer the
types. In that case you don't need to declare types
explicitly. But you can, if you want.
val joe = new Person("Joe", 35)
val age = joe.age
14. WITHEXPLICITTYPES
val joe: Person = new Person("Joe", 35)
val age: Int = joe.age
val olderJoe: Person = new Person(joe.name, age + 1)
joe greet olderJoe
def makeFriends(p1: Person, p2: Person): String =
"%s and %s became good friends".format(p1.name, p2.name)
makeFriends(joe, olderJoe)
15. INHERITANCE
abstract class A {
def call = "A"
}
trait B {
def call = "B"
}
trait C {
def call = "C"
}
class ABC extends A with B with C {
override def call = super.call
}
val abc = new ABC
println(abc.call) //prints C
16. ANONYMOUSCLASSES
trait Jedi {
def useForceOn(p: Person): String
def greet(p: Person) =
"May the force be with you, %s!".format(p.name)
}
trait ForceLightning {
def useForceOn(p: Person) =
"%s gets shocked with force lightning"
}
val obiWan = new Person("Obi Wan", 71) with Jedi {
override def greet(p: Person) = super.greet(p)
def useForceOn(p: Person): String =
"These are not the droids you're looking for, %s".format(p.name)
}
val anakin = new Person("Anakin", 27) with Jedi with ForceLightning {
override def greet(p: Person) =
"Join the dark side, %s!".format(p.name)
}
17. GENERICS
class Pair[A,B](val first: A, val second: B) {
def add[C](third: C) = new Triple(first, second, third)
}
class Triple[A,B,C](val first: A, val second: B, val third: C)
val p = new Pair("Joe", 36)
val t = p.add('MALE)
18. COMPANION OBJECT
class Pair[A, B](val first: A, val second: B)
object Pair {
def apply[A, B](first: A, second: B) = new Pair(first, second)
def empty[A, B] = new Pair[Option[A], Option[B]](None, None)
}
//Pair[String, Int]
val p1 = Pair("Joe", 36)
//Pair[Option[String], Option[Int]]
val p2 = Pair.empty[String, Int]
20. FP
Using side effect free functions for computations
Avoiding mutable data structures and variables
First-class functions
Recursion
21. FPBENEFITS
Side effect free functions are easy to test and
understand.
Functions are more reusable than classes
Makes concurrency and parallelism easier
More declarative and concise code
Stop worrying what objects got mutated between
method calls and instead start combining functions to
transform the input to the desired output
22. OOP+FP
OOP generally is about nouns (Person, Car, Record)
FP is about verbs (filter, map, reduce, group, zip)
OOP offers a way to structure
FP offers a way to compute
In large projects we benefit from both
23. FPUSECASES
Good fit:
Transforming data
Systems where you need concurrency and parallelism
Not a good fit:
UI (although, rumor has it that FRP makes UI
programming easy)
IO (read/write file, query the DB, make a HTTP request)
PS! Above scenarios are all good fit for Scala as you can
easily change programming paradigms
25. FUNCTIONSAS
PARAMETERS
filter, map and reduce are functions defined on the List
object
filter expects a function of A => Boolean
map expects a function of A => B
reduce expects a function of (A, A) => A
def isEven(x: Int) = x % 2 == 0
def pow3(x: Int) = x * x * x
def add(x: Int, y: Int) = x + y
val numbers = List(1, 2, 3, 4)
numbers.filter(isEven).map(pow3).reduce(add) //72
26. FUNCTION LITERALS
All FP languages support function literals since it's the
central part of the paradigm
val numbers = List(1, 2, 3, 4)
val evens = numbers.filter(n => n % 2 == 0)
val powOf3s = evens.map(n => n * n * n)
val sum = powOf3s.reduce((prev, next) => prev + next)
27. PLACEHOLDERS
You can use underscores as placeholders for one or
more parameters, so long as each parameter appears
only one time within the function literal.
val numbers = List(1, 2, 3, 4)
val evens = numbers.filter(_ % 2 == 0)
val powOf3s = evens.map(n => n * n * n) // here it's necessary
val sum = powOf3s.reduce(_ + _)
28. ASSIGNINGFUNCTIONSTO
VALUES
val evens = (_: Int) % 2 == 0
val pow3 = (n: Int) => n * n * n
val add = (x: Int, y: Int) => x + y
val numbers = List(1, 2, 3, 4)
val result = numbers.filter(evens).map(pow3).reduce(add)
29. FUNCTIONSAREOBJECTS
TOO
Every function implements the FunctionN trait. The apply
method allows us to use the function syntax.
val f = new Function2[Int, Int, Int] {
def apply(y: Int, x: Int): Int = x + y
}
f(1, 2)
((x: Int, y: Int) => x + y).isInstanceOf[Function2[Int, Int, Int]]
30. FUNCTIONSTHATRETURN
FUNCTIONS
It's quite common to write a function that produces a
new function
def powerOf(p: Int) = (n: Int) => Math.pow(n, p).toInt
val powerOf5 = powerOf(5)
val numbers = List(1, 2, 3, 4)
numbers.map(powerOf5) //1, 32, 243, 1024
numbers.map(powerOf(2)) //1, 4, 9, 16
33. EVERYDAYEXAMPLES
Transforming Collections from type A to B
Java:
public Collection<String> personsToNames(Collection<Person> persons) {
List<String> names = new ArrayList<String>();
for (Person p : persons) {
names.add(p.name);
}
return names;
}
35. EVERYDAYEXAMPLES
Filter elements from Collections based on criteria
Java:
public Collection<Person> filterAges(Collection<Person> persons, int threshol
List<Person> filtered = new ArrayList<Person>();
for (Person p : persons) {
if (p.age >= threshold) {
filtered.add(p);
}
}
return filtered;
}
41. RECURSION
Procedural way to calculate factorial
FP avoids loops with mutable state. Instead recursion is
often used.
def factorial(n: BigInt) = {
if (n == 0) 1
else {
var product: BigInt = 1
var i = n
while (i > 0) {
product *= i
i -= 1
}
product
}
}
def factorial(n: BigInt): BigInt =
if (n == 0) 1
else n * factorial(n - 1)
42. TAILCALLOPTIMIZATION
Previous recursive factorial consumes stack and will
throw StackOverflow for big numbers. A proper FP
language offers tail call optimization. In Scala we can use
the @tailrec annotation. The compiler will complain if it is
unable to make the function tail recursive.
def factorial(n: BigInt) = {
@tailrec def calc(n: BigInt, acc: BigInt): BigInt =
if (n == 0) acc else calc(n - 1, acc * n)
calc(n, 1)
}
43. FOR EXPRESSION
The for expression is a powerful tool in Scala that allows
you to:
iterate through a collection
perform nested iterations
filter out elements based on arbitrary conditions
produce new collections
44. FOR EXPRESSION EXAMPLE
def find(dir: File, predicate: String => Boolean): Seq[String] =
if (!dir.exists || !dir.isDirectory)
List.empty
else
for (f <- dir.listFiles if predicate(f.getName)) yield f.getName
find(new File("C:installedmaven3bin"), _.endsWith("bat"))
//ArraySeq(mvn.bat, mvnDebug.bat)
45. FOR EXPRESSION EXAMPLE
for (
i <- 'a' to 'c';
j <- 3 to 1 by -1
) yield (i, j)
//Vector((a,3), (a,2), (a,1), (b,3), (b,2), (b,1), (c,3), (c,2), (c,1))
46. IMMUTABLECOLLECTIONS
Immutability is one of the cornerstones of FP. The Scala
scala.collection.immutable package offers wide variety of
immutable collection data structures. If you really need
mutable collections then you need to use the
scala.collection.mutable package.
47. val nums = 1 to 10
println(nums) //Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val divBy3 = nums.filter(_ % 3 == 0)
println(divBy3) //Vector(3, 6, 9)
//nums did not change
println(nums) //Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
import scala.collection.mutable
val list = mutable.MutableList[Int](1, 2, 3)
println(list) //MutableList(1, 2, 3)
list(1) = 6
println(list) //MutableList(1, 6, 3)
48. LOAN PATTERN
Control abstraction pattern. The function accepts a
resource and a function. The function takes care of the
common repetitious work while borrowing the resource
to the outer function to do something interesting with it.
import java.io._
def using[A <: Closeable, B](resource: A)(op: A => B) = {
try op(resource)
finally resource.close()
}
val file = File.createTempFile("scala", "loan-pattern")
using(new FileWriter(file)) {
w => {
w.write("Can I loan some money please?")
w.flush()
}
}
val line = using(new BufferedReader(new FileReader(file))) {
r => r.readLine()
49.
50. FP+OOP=FUNCTIONAL
OBJECTS
FP and OOP can be used together to produce code that is
concise, reusable and well structured. Let's implement a
simple immutable Rational number class. First in Java and
then in Scala.
51. JAVAIMPLEMENTATION
public final class Rational {
public final int numerator;
public final int denominator;
public Rational(final int numerator) {
this(numerator, 1);
}
public Rational(final int numerator, final int denominator) {
if (denominator == 0) {
final String msg = "Denominator must not be 0";
throw new IllegalArgumentException(msg);
}
this.numerator = numerator;
this.denominator = denominator;
}
public Rational add(final Rational that) {
final int n = this.numerator * that.denominator +
that.numerator * this.denominator;
final int d = this.denominator * that.denominator;
final int gcd = ArithmeticUtils.gcd(n, d);
return new Rational(n / gcd, d / gcd);
}
52. USINGTHEJAVARATIONAL
This API looks a lot like BigInteger or BigDecimal. It's
verbose. In Java can we write 1 + 1 or 3 - 2. But we can't
use + and - with Rationals. In mathematical sense
Rational is also a number. Wouldn't it be dreamy if we
could use something like:
new Rational(1).substract(new Rational(3,5)).add(new Rational(2,3));
(1 - Rational(3,5)) + Rational(2,3)
53. SCALAIMPLEMENTATION
final class Rational(val numerator: Int, val denominator: Int) {
if (denominator == 0)
throw new IllegalArgumentException("Denominator must not be 0")
def +(that: Rational) = {
val n = this.numerator * that.denominator +
that.numerator * this.denominator
val d = this.denominator * that.denominator
val gcd = ArithmeticUtils.gcd(n, d)
new Rational(n / gcd, d / gcd)
}
def -(that: Rational) = this + (-that)
def unary_- = new Rational(-numerator, denominator)
override def toString = numerator + "/" + denominator
}
object Rational {
def apply(numerator: Int, denominator: Int) =
new Rational(numerator, denominator)
def apply(numerator: Int) =
55. SCALAFPGUIDELINES
Don't use the var keyword. Use vals.
Don't use the while keyword. Use higher order
functions on collections or the for expression or
recursion.
Avoid the scala.collection.mutable package.
Don't use null. Use Option[T] - Some(value), None.
Scala is a powerful language. You can write code that
looks like Java. You can write code that looks like
Haskell. FP and OOP are not mutually exclusive. FP +
OOP is actually a nice combination, but try to minimize
state and mutability.
56. ABALANCEDATTITUDEFOR
SCALAPROGRAMMERS
“Prefer vals, immutable objects, and
methods without side effects. Reach for
them first. Use vars, mutable objects, and
methods with side effects when you have
a specific need and justification for them.”
- Programming In Scala 2nd ed
58. PATTERN MATCHING
FEATURES
Pattern matching in Scala is basically Java switch-case on
steroids. With pattern matching we can:
Match values
Match case classes
Destructure and match collection-like structures
Destructure and match arbitrary data structures using
our own extractors
Let's look at pattern matching in more detail using
examples.
59. PATTERN MATCHINGON
VALUES
val turn = 1
def reallyGo = false
val result = turn match {
case 1 => "Ready"
case 2 | 3 => "Steady" //logical or
case 4 if reallyGo => "Go!" //guard
case _ => "Wut???" //catch all case
}
61. PATTERN MATCHINGON
CASECLASSES-AST
trait AST[T]
case class Expression[T](fn: (T, T) => T,
left: AST[T],
right: AST[T]) extends AST[T]
case class Value[T](value: T) extends AST[T]
def divide(x: Int, y: Int) = x / y
def multiply(x: Int, y: Int) = x * y
def add(x: Int, y: Int) = x + y
def subtract(x: Int, y: Int) = x - y
val ast =
Expression(divide,
Expression(add,
Expression(multiply, Value(2), Value(5)),
Expression(subtract, Value(3), Value(1))),
Value(3))
62. PATTERN MATCHINGON
LISTS
Most collection-like data structures in standard library
have an extractor. Extractor allows us to destruct a data
structure and match the inner elements.
val list = List("John", "Smith", 36, List("Linda", "Smith", 32))
val msg = list match {
case name :: _ :: _ :: List(spouse :: _) =>
name + " is married to " + spouse
case _ => "Cannot make sense of the list"
}
println(msg) //John is married to Linda
63. PATTERN MATCHINGON
EXCEPTIONS
try {
throw new NullPointerException()
} catch {
case ioEx: IOException => // do something else
case e @ (_ : NullPointerException | _: IllegalArgumentException) =>
// common logic for NPE and IAE
case unexpectedEx => // catch all case
} finally {
//clean up
}
64. CUSTOMEXTRACTOR
By writing our own extractor we can destruct and pattern
match other data structures in a way that makes sense in
our problem domain. For example some API returns
URLs as Strings. We can write an extractor that allows us
to destructure the URL String.
65. object Url {
def apply(secure: Boolean,
domain: String,
topLvlDomain: String) = {
val protocol = if (secure) "https" else "http"
protocol + "://" + domain + "." + topLvlDomain
}
def unapply(url: String) = {
def isHttp(p: String) = p.startsWith("http")
def isSecure(p: String) = p.endsWith("s")
url.split("://") match {
case Array(protocol, domainPart) if isHttp(protocol) =>
domainPart.split(".") match {
case Array(domain, topLvlDomain) =>
Some((isSecure(protocol), domain, topLvlDomain))
case _ => None
}
case _ => None
}
}
}
val msg = "https://ishisystems.com" match {
case Url(true, domain, _) =>
"Welcome to secure '%s' network".format(domain)
case url => url + " is not allowed"
}
67. PIMPMYLIBRARY
In real world we are mostly dealing with 3rd party APIs.
Some APIs are well designed, some are not. We cannot
modify them to make them easier to use with our own
code. However, in Scala we can use implicit conversions
to make those 3rd party APIs more richer and expressive.
68. RICHDATE
java.util.Date with accessories
import java.util.{Calendar, Date}
class RichDate(val date: Date) {
def isSameDay(comparedDate: Date): Boolean = {
val cal = Calendar.getInstance()
def getYearAndDay(d: Date) = {
cal.setTime(d)
val year = cal.get(Calendar.YEAR)
val day = cal.get(Calendar.DAY_OF_YEAR)
(year, day)
}
val (y1, d1) = getYearAndDay(date)
val (y2, d2) = getYearAndDay(comparedDate)
y1 == y2 && d1 == d2
}
}
object RichDateConversions {
implicit def date2RichDate(d: Date) = new RichDate(d)
69. USINGRICHDATE
Now we can use java.util.Date in ways the original
authors did not think of.
import RichDateConversions.date2RichDate
val d1 = new Date
val oneHour = 60 * 60 * 1000
val d2 = new Date(d1.getTime + oneHour)
d1 isSameDay d2
70. DON'TOVERUSEIMPLICITS
Try to minimize the use of implicits. It makes the code
look like it's doing something magical. Implicits are even
known to confuse your IDE. If even your IDE can get
confused then a developer can definitely get confused.
72. SCALA&XML
Scala has good support for XML. You can:
Write XML literals
Write code inside XML literals
Query the XML with a XPath-like API
Pattern match XML
76. PATTERN MATCHINGON
XML
XML pattern matching is somewhat unuseful because of
the significant whitespace.
def xmlToPerson(personXML: Seq[Node]): Option[Person] = personXML match {
case <person><name>{name}</name><age>{age}</age><spouse>{person}</spouse></
Some(new Person(name.text, age.text.toInt, xmlToPerson(person)))
case <person><name>{name}</name><age>{age}</age></person> =>
Some(new Person(name.text, age.text.toInt, None))
case _ => None
}
xmlToPerson(xml.Utility.trim(personXML))
78. PARALLELISMAND
CONCURRENCY
In Scala you can use all the tools from java.util.concurrent
in addition to some extra goodies that Scala offers:
Parallel collections
Actor library - deprecated from core Scala and moved
to Akka
79. PARALLELCOLLECTIONS
Most of the sequential Scala collections can be turned
into a parallel counterpart with the par function. Some
points about parallel collections:
The main use case for parallel collections is when you
need to apply a side-effect free computation to each
element of the collection and the order does not
matter.
The computation should be non-trivial to see
performance gains.
Parallel collections are implemented on top of the Java
fork-join framework.
81. def factorial(n: BigInt) = {
@tailrec def calc(n: BigInt, acc: BigInt): BigInt =
if (n == 0) acc else calc(n - 1, acc * n)
calc(n, 1)
}
val numbers = BigInt(100000) to BigInt(100012)
def measure(work: => Unit) {
val start = System.currentTimeMillis
work
val end = System.currentTimeMillis
println(end - start)
}
// my machine 4 cores
// 12 computations
// 12 / 4 => should get roughly 3x increase in performance
// sequential
measure(numbers.map(factorial)) // ~3 minutes
// parallel
measure(numbers.par.map(factorial)) // ~1 minute
82. ACTORS
The actor model represents a share-nothing message-
passing programming model that is easier to understand
for developers than shared mutable data with locks.
Actor is an object that has an inbox to receive messages.
Actor can process only one message at a time. Actors
communicate with each other by sending immutable
message objects using the ! method.
83. ACTOR BENEFITS
No shared data
No locks & synchronization
Asynchronous message passing
Actors can share a thread pool
Combine actors with java.util.concurrent classes. For
example, multiple actors can share a
ConcurrentHashMap
Akka offers many other benefits such as support for
distributed actors and fault tolerance, but that is
another subject
89. MESSAGES
Let's define the messages as case classes so they can be
pattern matched by actors.
case class StartSprint()
case class Develop(story: String)
case class FixBug(story: String)
case class DevelopmentDone(story: String)
case class Test(story: String)
case class TestingDone(story: String, bug: Boolean)
case class Release(story: String)
case class NewFeature(story: String)
90. DEVELOPER ACTOR
class Developer(val name: String) extends Actor {
def receive = {
case Develop(story) => develop(story)
case FixBug(story) => fixBug(story)
}
def develop(story: String) {
println(name + " develops " + story)
tracker ! DevelopmentDone(story)
}
def fixBug(story: String) {
println(name + " fixes bug in " + story)
tracker ! DevelopmentDone(story)
}
}
91. TESTER ACTOR
class Tester(val name: String) extends Actor {
def receive = {
case Test(story) => test(story)
}
def test(story: String) {
val foundBug = Random.nextBoolean()
val log =
if (foundBug) name + " found a bug in " + story
else name + " verified that " + story + " works"
println(log)
tracker ! TestingDone(story, foundBug)
}
}
92. RELEASEMANAGER ACTOR
class RM extends Actor {
def receive = {
case Release(story) => {
println("RM deployed " + story + " to production")
client ! NewFeature(story)
}
}
}
93. CLIENTACTOR
class Client extends Actor {
def receive = {
case NewFeature(feature) => {
println("Client tries out " + feature +
" and is satisfied with it")
}
}
}
94. TRACKER ACTOR
As seen from the diagrams the Tracker actor coordinates
the work between developers, testers and the release
manager.
class Tracker(stories: Seq[String]) extends Actor {
/**
* Map of Story -> (Dev, Tester)
* Each story gets assigned a developer and a tester.
*/
val sprintPlan = {
val numberOfStories = stories.size
def repeat[T](resources: Seq[T]) =
Stream.continually(resources).flatten.take(numberOfStories)
val assignments =
(stories, repeat(developers), repeat(testers)).zipped.toList
assignments.map {
case (story, dev, tester) => (story, (dev, tester))
}.toMap
}
95. APPCONTEXT
Container that creates the actor system.
object AppContext {
println("Setting up AppContext...")
val actorContext = ActorSystem("SprintAppActors")
val tracker = {
val stories = for (i <- 1 to 10) yield "Feature-" + i
toActor(new Tracker(stories), "tracker")
}
val developers = toActors(List("Rasmeet", "Viral", "Dhruv"),
(name: String) => new Developer(name), "developers")
val testers = toActors(List("Dilip", "Shaishav"),
(name: String) => new Tester(name), "testers")
val val releaseManager = toActor(new RM, "RM")
val client = toActor(new Client, "Client")
def toActors[T](xs: Seq[T], fn: T => Actor, groupName: String) =
xs.zipWithIndex.map {
case (x, i) => toActor(fn(x), groupName + "-" + i)
97. SAMPLEOUTPUT
Setting up AppContext...
Starting sprint
Rasmeet develops Feature-10
Viral develops Feature-5
Dhruv develops Feature-6
Dhruv develops Feature-9
Dhruv develops Feature-3
Rasmeet develops Feature-1
Viral develops Feature-2
Rasmeet develops Feature-7
Viral develops Feature-8
Rasmeet develops Feature-4
Shaishav found a bug in Feature-5
Dilip verified that Feature-6 works
Shaishav verified that Feature-2 works
Shaishav verified that Feature-8 works
Dilip found a bug in Feature-10
Dilip verified that Feature-9 works
Dilip found a bug in Feature-3
Dilip found a bug in Feature-1
Dilip verified that Feature-7 works
Dilip found a bug in Feature-4
Viral fixes bug in Feature-5
RM deployed Feature-6 to production
Rasmeet fixes bug in Feature-10
Rasmeet fixes bug in Feature-1
99. OBSERVATIONS
IDE. Intellij is currently hands down the best IDE for
Scala. Eclipse Scala plugin is coming along but still has
too many bugs and quirks to make it worthwile. Also,
Eclipse in general has been getting slower with each
successive release.
100. OBSERVATIONS
Code is really concise. Passing functions to other
functions is awesome. I really do feel encouraged to write
immutable classes and avoid changing state as Scala
makes it easy to write without ceremony. The downside
is that developers can write too concise code that
becomes hard to read. Best advice is to try to keep the
code simple even if it means writing a bit more code.
101. OBSERVATIONS
It's easy to interop with existing Java code. However, it's
not so easy to call some of the Scala code from Java.
102. OBSERVATIONS
Scala method definitions can be hard to understand.
// Wut me supposed to do wit it???
scan[B >: A, That](z: B)(op: (B, B) ? B)(implicit cbf: CanBuildFrom[List[A],
// Huh?
:+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That
103. OBSERVATIONS
When the team uses Scala code reviews become very
important. People with different programming
backgrounds will use Scala differently. Haskell guys will
write it as if it were Haskell. Java guys will write it as if it
were Java. In general it's better to lean towards FP
principles as it tends to produce code that is more
reusable and is easier to understand in isolation.
104. OBSERVATIONS
Scala really shines when it comes to mixing OOP with FP,
but it's not a good language to learn FP. You will benefit a
lot if you get your hands dirty with Haskell or Clojure and
then come back to Scala with a new perspective.
105. SCALAADOPTION
STRATEGY
Decide as a team whether you want to use Scala. This
is key.
Start by writing test cases in Scala. You will be only
using basic vanilla Scala, but at least it will give you
confidence that you can effectively use Scala together
with the existing code base.
If you're introducing a new backend component or
breaking up an existing monolithic component give
Scala a chance.
106. LEARNINGMATERIALS
Free online Scala course on Coursera
Programming In Scala 2nd ed
Scala in Depth
Functional Programming in Scala
Twitter's Scala School
Official documentation
Akka documentation
Scala Days 2013 videos
InfoQ Scala presentations
99 Scala problems