I used these slides for a Scala workshop that I gave. They are based on these: http://www.scala-lang.org/node/4454. Thanks to Alf Kristian Støyle and Fredrik Vraalsen for sharing!
1. Scala workshop
Created by
Fredrik Vraalsen (fredrik@vraalsen.no)
and
Alf Kristian Støyle (alf.kristian@gmail.com)
Adapted
Bert Van Vreckem (bert.vanvreckem@hogent.be)
2. If I were to pick a language to use
today other than Java, it would be
Scala.
James Gosling
3. Scala, it must be stated, is the current heir
apparent to the Java throne. No other language
on the JVM seems as capable of being a
"replacement for Java" as Scala, and the
momentum behind Scala is now unquestionable.
Charlies Olivier Nutter - JRuby lead
4. Though my tip though for the long term
replacement of javac is Scala. I'm very
impressed with it! I can honestly say if
someone had shown me the Programming
in Scala book by by Martin Odersky, Lex
Spoon & Bill Venners back in 2003 I'd
probably have never created Groovy.
James Strachen
5. public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
class Person(var age: Int,
var name: String)
6. List<Person> persons = ...
List<Person> adults = new LinkedList<Person>();
List<Person> kids = new LinkedList<Person>();
for (Person person : persons) {
if (person.getAge() < 18) {
kids.add(person);
} else {
adults.add(person);
}
}
val (kids, adults) =
persons.partition(_.age < 18)
7. String s = "!em esreveR";
System.out.println(s.reverse());
val s = "!em esreveR"
println(s.reverse)
=> Reverse me!
16. Variables
Scala: Java:
val s = "Hello World" public final String s =
"Hello World";
var i = 1
public int i = 1;
private var j = 3
private int j = 3;
18. Classes and constructors
Scala: Java:
class Person(val name: public class Person {
String) private final String
name;
public Person(String
name) {
this.name = name;
}
public String getName() {
return name;
}
}
19. Traits (= Interface + Mixin)
Scala: Java:
trait Shape { interface Shape {
def area: Double public double area();
} }
class Circle extends Object public class Circle extends
with Shape Object implements Shape
20. No “static” in Scala
Scala: Java:
object PersonUtil { public class PersonUtil {
val ageLimit = 18 public static final int
AGE_LIMIT = 18;
def countPersons( public static int
persons: List[Person]) = countPersons(
... List<Person> persons)
} {
...
}
}
23. Varargs
def foo(values: String*){ } public void foo(String... values){ }
foo("bar", "baz") foo("bar", "baz");
val arr = Array("bar", "baz") String[] arr =
new String[]{"bar", "baz"}
foo(arr: _*) foo(arr);
24. (Almost) everything is an expression
val res = if (foo) x else y
val res = for (i <- 1 to 10) yield i
// List(1, ..., 10)
val res = try { x } catch { ...; y }
finally { } // x or y
30. REPL: Read-Eval-Print Loop
● Command line shell for on-the-fly execution of
Scala statements
●
$ cd ${SCALA_HOME}/bin
$ scala
● Windows, e.g. C:Program FilesScala 2.8.0
● Linux, e.g. /opt/scala or /usr/local/scala
31. IDE
● They are all !#$&§? compared to what you are
used to with Java support
● Netbeans (very good) but bad in other areas...
● IntelliJ IDEA (pretty good) but slow compilation,
bonus community edition is free (with Scala)
● Eclipse (not all that good) but very fast when
working
32. Netbeans 6.9.x installation
● Download plugins
http://sf.net/projects/erlybird/files/nb-scala/6.9v1.1.0/
● In NetBeans "Tools" | "Plugins",
● click on "Downloaded" tab title,
● click on "Add Plugins..." button,
● choose the directory where the Scala plugins are unzipped,
● select all listed *.nbm files, following the instructions.
● Set ${SCALA_HOME} or %SCALA_HOME% environment variable
● Edit ${NETBEANS_HOME}/etc/netbeans.conf
● Add "-J-Dscala.home=/opt/scala" to
netbeans_default_options
● More info: http://wiki.netbeans.org/Scala
33. Tasks (20 min)
● Run REPL
● Windows: %scala_home%/bin/scala
● Linux: scala or /opt/scala/bin/scala
● Execute a few statements
● Copy & unpack
'HeliumPublicCursussenNavorming
ScalaScalaTraining.zip'
● into 'DocumentenNetbeansProjects' (Windows)
● Into '/home/student/NetbeansProjects' (Linux)
34. Tasks (20 min)
● Run unit test in IDE (JUnit 4)
● Open the 'ScalaTraining' project in NetBeans
● Try to compile and test the project (should work)
● scalaexamples.intro.MyFirstTest
– Create a failing test
– Make it run
● Make the two classes scalaexamples/intro/HelloWorld.scala
print “Hello world”. What is the difference?
● Remove comments from @Test for the methods in
scalaexamples/intro/CreateStuffTest.scala. Make tests pass.
36. First class functions
val even = Function[Int, Boolean] {
def apply(i: Int) = i % 2 == 0
}
val even: (Int => Boolean) = (i: Int) => i % 2 == 0
val even = (i: Int) => i % 2 == 0
even.apply(42) // true
even(13) // false
37. First class functions
val numbers = List(1, 2, 3, 4, 5)
numbers.filter(even) // List(2, 4)
numbers.filter((i: Int) => i > 2) // List(3, 4, 5)
numbers.filter(i => i > 2) // List(3, 4, 5)
numbers.filter(_ > 2) // List(3, 4, 5)
38. Collections
numbers.filter(i => i > 2) // List(3, 4, 5)
numbers.find(i => i > 2) // Some(3)
numbers.exists(i => i > 2) // true
numbers.forall(i => i > 2) // false
numbers.map(i => i * 2) // List(2, 4, 6, 8, 10)
numbers.foldLeft(0) { (a, b) => a + b } // 15
39. Closures
val people = List(Person(“Alf”), Person(“Fredrik”))
var name = “Fredrik”
val nameFilter = (p: Person) => p.name == name
people.filter(nameFilter) // Person(“Fredrik”)
name = “Alf”
people.filter(nameFilter) // Person(“Alf”)
40. Tasks (30 min)
● Open the 'first-class-functions' project
● Tests in package
scalaexamples.firstclassfunctions
● Add @Test to one and one method
● Follow instructions in the code
● Make the tests pass
42. myObject match {
case 1 => println("First was hit")
case 2 => println("Second was Hit")
case _ => println("Unknown")
}
43. myObject match {
case i: Int => println("Found an int")
case s: String => println("Found a String")
case _ => println("Unknown")
}
44. myObject match {
case i: Int => println("Found an int")
case s: String => println("Found a String")
case other => println("Unknown " + other)
}
45. myObject match {
case i: Int if i == 1 => println("Found an int")
case s: String => println("Found a String")
case other => println("Unknown " + other)
}
46. val res = myObject match {
case i: Int if i == 1 => "Found an int"
case s: String => "Found a String"
case other => "Unknown " + other
}
47. val res = myObject match {
case (first, second) => second
case (first, second, third) => third
}
48. val mathedElement = list match {
case List(firstElement, lastElement) => lastElement
case List(firstElement, _ *) => firstElement
case _ => "failed"
}
50. public static Integer getSecondOr0(List<Integer> list) {
if (list != null && list.size() >= 2) {
return list.get(1);
} else {
return 0;
}
}
def second_or_0(list:List[Int]) = list match {
case List(_, x, _*) => x
case _ => 0
}
51. Case classes
● Class types that can be used in pattern
matching
● Generated into your class:
● equals
● hashCode
● toString
● getters (and optionally setters)
● ++
52. abstract class Person(name: String)
case class Man(name: String) extends Person(name)
case class Woman(name: String, children: List[Person])
extends Person(name)
53. p match {
case Man(name) => println("Man with name " + name)
case Woman(name, children) => println("Woman with name "
+ name + " and with " +
children.size + " children")
}
54. val regex = """(d+)(w+)""".r
val myString = ...
val res: String = myString match {
case regex(digits, word) => digits
case _ => "None"
}
55. val regex = """(d+)(w+)""".r
val myString = ...
val res: Option[String] = myString match {
case regex(digit, word) => Some(digit)
case _ => None
}
56. The Option type, never again
NullPointerException
● Option has two possible values
● Some(value)
● None
val someOption: Option[String] = Some("value")
val noOption: Option[String] = None
57. def getValue(s: Any): Option[String]
getValue(object) match {
case Some(value) => println(value)
case None => println("Nothing")
}
val result = getValue(object).getOrElse("Nothing")
58. Tasks (30 min)
● Open the 'pattern-matching' project
● Tests in package
scalaexamples.patternmatching
● Add @Test to one and one method
● Follow instructions in the code
● Make the tests pass
60. Annotations – not marker interfaces
@serializable class Person
@SerialVersionUID(1) class Person
@cloneable class Person
@remote class Service
61. object
● object is a “singleton” class
● Call may look like static method calls in Java
● Can inherit from other classes and traits
● Can be passed as a reference
object MyObject {
def foo = "bar"
}
var baz = MyObject.foo
val personObject = MyObject
baz = personObject.foo
62. Companion object
● Can read the companion class' private fields
● Has to be in the same source file
class Person(private val age: Int)
object Person {
def getPersonAge(p: Person) = p.age
}
val personInstance = new Person(30)
val age = Person.getPersonAge(personInstance)
63. Magical apply
class Person private(val age: Int)
object Person {
def apply(age: Int) = new Person(age)
}
var personInstance = Person.apply(30)
personInstance = Person(30)
64. Not built in, clever use of apply
val myList = List(1, 2, 3)
val mySet = Set(1, 2, 3)
val myMap = Map(1 -> "one", 2 -> "two")
65. Constructors
● Always one primary constructor
● Parameters are automatically instance variables
● Class “body” is the primary constructors content
● Auxiliary constructors MUST call or chain to primary
constructor
class MyClass(myString: String, myInt: Int)
val myOtherInt = 10
println("In main body")
}
72. The Ordered trait
class Person(private val age: Int)
extends Ordered[Person] {
def compare(other: Person) =
this.age - other.age
}
val person1 = new Person(21)
val person2 = new Person(31)
person1 < person2 // true
person1 <= person2 // true
person1 >= person2 // false
73. “Dynamic mixins”
class Person(age: Int) {
override def toString = "my age is " + age
}
trait MyTrait {
override def toString = "I am sure " +
super.toString
}
val person = new Person(30) with MyTrait
println(person)
=> I am sure my age is 30
74. is-a vs has-a
● Inheritance as usual
● Easier to implement has-a
● Inheritance vs composition
75. Tasks (30 min)
● Open 'oo-traits' project
● Tests under scalaexamples
● companionobject
● inheritance
● traits
● Add @Test to one and one method
● Follow instructions in code
● Make tests pass
78. Functional programming
● Purity
● Mathematical functions have no side effects
● f(x) = 2x + 3
● y = sin(x)
● Mathematical functions always give same result
for same input
● Only immutable objects (and object graphs)
79. In practice
● All fields must be immutable
● All methods must return something
● No side-effects from method calls
●
println("...") is also a side effect!
80. List
● head :: tail
● Prepends to the head of the list
● Other operations (filter, map, remove,
partition...) returns a new List instance
85. Refactoring imperative style code
● Side effect: printing table
● Functional style: return String
● Harder to test
● How to test result of println()?
● While loop & vars
● Functional style: val, for expressions, helper
functions
● Helper functions can be tested separately
86. Functional style multiplication table
def makeRowSeq(row: Int) =
for (col <- 1 to 10) yield {
val prod = (row * col).toString
val padding = " " * (4 - prod.length)
padding + prod
}
def makeRow(row: Int) = makeRowSeq(row).mkString
def multiTable = {
val tableSeq =
for (row <- 1 to 10)
yield makeRow(row)
tableSeq.mkString("n")
}
87. A closer look at Collections
● Overview of collection traits
http://www.decodified.com/scala/collections-api.xml
89. Iterable
● Abstract method iterator
● Default implementation of foreach:
def foreach[U](f:Elem => U): Unit = {
val it = iterator
while (it.hasNext) f(it.next())
}
● Subclasses may override
● Some concrete methods
● Subcollections, "zippers", comparison
90. Seq, IndexedSeq, LinearSeq
● Sequence:
● iterable that has a length,
● elements have fixed index position, starting with 0
● Operations:
● Indexing (apply), search, addition, update, sorting,
reversal, comparison, set operations
● IndexedSeq, LinearSeq
● No new operations, but different performance
– LinearSeq: efficient head, tail
– IndexedSeq: efficient apply, length
91. Sets, SortedSet
● Set = iterable that contain no duplicates
● Operations for tests, addition, removal, set
operations
● SortedSet
● Iterator/foreach visit elements in given ordering
● Default implementation: binary tree
92. Maps
● Map = collection of pairs of keys and values
e.g. Map("x" -> 24, "y" -> 25, "z" -> 26)
● Operations for lookup, addition/update, removal,
subcollections, transformations
97. Higher order functions
Short summary of first class functions:
val even: (Int => Boolean) = (i: Int) => i % 2 == 0
Same type definition:
def test(numbers: List[Int], f: Int => Boolean) = ...
Call:
test(List(1, 2, 3), (i: Int) => i % 2 == 0)
98. Higher order functions
def test(numbers: List[Int],
f: Int => Boolean) =
numbers.map(tall => f(tall))
// List[Boolean]
99. Higher order functions
Functions with several parameters must list them
in parenthesis:
def test(l: List[String],
f: (Int, String) => Boolean)
100. call-by-value vs. call-by-name
● by-value: expressions are evaluated before
being passed to the function
● by-name: expressions evaluated inside function
● nice when computationally expensive
● possible to create nice APIs
105. def using[T <: { def close() }, A]
(closeable: T)
(f: T => A) = {
try {
f(closeable)
} finally {
if (closeable != null) {
try {
closeable.close()
}
catch {
case e: Exception =>
// Do something clever!?
}
}
}
}
106. Tasks (30 min)
● Open 'higher-order-functions' project
● Tests in scalaexamples.higherorderfunctions
● Add @Test to one and one method
● Implement missing functions in PersonFilter
and so on.
● Follow instructions in code
● Make tests pass