(how) can we benefit from adopting scala?
TRANSCRIPT
(How) Can we benefitfrom
adoptingScala
?
Tomasz Wróbel, 2011-01-24
in other words what Scala has to offer
Few quick facts
• Strong typing• Static typing• Can be both interpreted and compiled into
bytecode and run on top of a JVM (among others)
• Interoperability with Java• No primitive types – everything is an object
Productivity gain
• Concise, expressive → achieve more with less• Reduced boilerplate code – semicolons, dots,
parentheses, return – these are often optional (depending on the context)
• Extensive type inference• ½ LOC at least• Code is read more frequently than written
Support for both functional andobject-oriented programming paradigms
• Code blocks (anonymous functions)• Higher-order functions (ones that produce or
consume functions)• Closures• Sophisticated collection library
REPL
Read-Evaluate-Print Loop
scala> val s = "Hello!"
s: java.lang.String = Hello!
scala> println(s)
Hello!
REPL:help print this help message
:load <arg> load and interpret a Scala file
:sh <line> fork a shell and run a command
scala> :sh ls
stdout: List[String] = List(Desktop, Documents, Downloads, Library, Movies, Music, Pictures, Public, Sites)
How difficult is it to switch?Java → Scala
// class
public class A {}
// variable
private int i = 0;
// function
public String fun(String s) {
return s;
}
class A {}
private var i: Int = 0;
def fun(s: String): String = {
return s;
}
Variables
• everything is either val or var• val – immutable• var – mutable
val x: Int = 1; x = x + 1
var y = 1; y = y + 1
val set = new java.util.HashSet[String]
set.add("abc")
Type inferenceval i: Int = 1
def upper(s: String): String = s.toUpperCase
val list: List[Int] = List[Int](1,2,3)
note: there are situations where type is required, e.g. recursive methods
operators = functions
• 1 + 2 is actually (1).+(2)• operators overloading• possibility to define new data types
Eurocase class Euro(euros: Int, cents: Int = 0) {
def +(e: Euro) = Euro(
euros + e.euros + ((cents + e.cents) / 100),
(cents + e.cents) % 100
)
}
case class Cent(cents: Int)
assertThat(Euro(5) + Euro(5), is(Euro(10)))
assertThat(Euro(4) + Cent(99), is(Euro(4, 99)))
Rangesval oneToTen = 1 to 10
assertThat(oneToTen.size, is(10))
val evenNumbers = 2 to 10 by 2
assertThat(evenNumbers.size, is(5))
val alphabet = 'a' to 'z'
assertThat(alphabet.size, is(26))
Tuples
are typedscala> val tuple = ("CODE", 31)
tuple: (java.lang.String, Int) = (CODE,31)
Tuples
are convenient way to return multiple values from a function
def analyzeString(s: String): (String, Int) = {
(s.toUpperCase, s.length)
}
scala> val result = analyzeString("abc")
result: (String, Int) = (ABC,3)
Controlling executionsfor (i <- 0 to 9) {
print(i)
}
for (i <- 0 to 9) {
if (i % 2 == 0) println("even: " + i) else println("odd: " + i)
}
Controlling executionsvar i = 0
while (i < 10) {
print(i)
i += 1
}
Raw strings// given
val s = """|first line
|second "B" line
|third line""".stripMargin
// then
assertThat(s, is("first line\nsecond \"B\" line\nthird line"))
Raw stringsassertThat("1234".matches("""\d*"""), is(true))
assertThat("12ab".matches("""\d*"""), is(false))
new File("""c:\some\path\to\file.txt""").exists
Making assertions@Test(expected = classOf[IllegalArgumentException])
def convenientAssertions {
val i = -1
require(i > 0)
}
Objects equality
• == is Java's equals()• eq()is Java's ==
Importsimport java.util.Date
import java.text.{DateFormat, SimpleDateFormat}
import java.math._
// static imports
import java.util.Arrays.asList
import java.util.Collections._
// aliasing
import java.sql.{Date => SqlDate}
Tail recursion
compiler optimizes tail-recursive functions to loops → preventing stack overflow errors
@tailrec
final def fact(n: Int, acc: Long = 1): Long = {
if (n < 1) acc else fact(n - 1, n * acc)
}
@tailrec – optional, compiler hints you if on not tail-recursive function
There is always an Optioni.e. null alternative
scala> def map = Map("PL" -> "Poland")
scala> val country: Option[String] = map.get("PL")
country: Option[String] = Some(Poland)
scala> val res = country.get
res: String = Poland
scala> val res = country.getOrElse("default")
res: String = Poland
scala> val country: Option[String] = map.get("UK")
country: Option[String] = None
scala> val res = country.getOrElse("default")
res: String = default
scala> val res = country.get
java.util.NoSuchElementException: None.get
Implicits
Allow to add functionality to existing classesval past = 2 days ago // 2.days(ago)
val future = 5 days later // 5.days(later)
println(new java.util.Date)
println(past)
println(future)
Sun Jan 23 22:18:51 GMT 2011
Fri Jan 21 22:18:51 GMT 2011
Fri Jan 28 22:18:51 GMT 2011
class, object, trait
• class – holds instance methods and fields• object – companion object – singleton
object, holds class (static) methods• trait – partial class implementation or
interface with implementation, it is possible to mixin multiple traits
class and objectclass Article private(title: String)
object Article {
def newInstance(title: String) = new Article(title)
}
object App {
def main(args: Array[String]) {
val article = Article.newInstance("a Title")
}
}
Traitstrait SingleJournalRelease extends PressRelease {
override def validate = println("articles form the same journal")
}
trait IssueRelease extends PressRelease {
override def header = print("Issue Press Release containing ")
}
trait AopRelease extends PressRelease {
override def header = print("AOP Press Release containing ")
}
class AcademicJournalAopPressRelease extends AopRelease with SingleJournalRelease
class ResearchJournalIssuePressRelease extends IssueRelease with SingleJournalRelease
Anonymous functionsval list = List(1, 2, 3)
val odds = list.filter((each: Int) => each % 2 != 0)
assertThat(odds, is(List(1, 3)))
Functions
Functions can be assigned to variables and be passed as parameters
val complicatedProcessing = (s: String) => s.toUpperCase
assertThat(complicatedProcessing("abc"), is("ABC"))
val list = List("a", "b", "c")
val result = list.map(complicatedProcessing)
assertThat(result, is(List("A", "B", "C")))
Local functions
Functions can be nested
def outer {
def inner(s: String) = println("inner " + s)
inner("outer")
}
Passing parameters to functionsscala> def fun(s: String) = println(s)
fun: (s: String)Unit
scala> fun("abc")
abc
scala> fun{"abc"}
abc
Curried functions
Currying = turning a function that takes two arguments into a chain of functions that take one argument
def sum(x: Int, y: Int) = x + y
def multiple(x: Int)(y: Int) = x * y
scala> val result = sum(1, 2)
result: Int = 3
scala> val result = multiple(1)(2)
result: Int = 2
Adding new control structureswriteToFile("output.txt") {
writer => writer.println("abc")
}
def writeToFile(fileName: String)(block : PrintWriter => Unit) = {
val writer = new PrintWriter(new File(fileName))
try { block(writer) } finally writer.close()
}
Implementing unlessscala> def unless(cond: => Boolean)(body: => Unit): Unit =
| if (!cond) {
| body
| }
unless: (cond: => Boolean)(body: => Unit)Unit
scala> unless(1 > 2) {
| print("1 is not greater than 2")
| }
1 is not greater than 2
Closures// given
val base = 100
def closeAroundBase(i: Int) = {
i + base
}
// then
assertThat(closeAroundBase(10), is(110))
Why is closure useful?val patternProvidedByUser = "L.*"
val data = List("London", "Oxford", "Leeds")
val result = data.filter(_.matches(patternProvidedByUser))
result: List[java.lang.String] = List(London, Leeds)
Case classescase class Article(var doi: String, var title: String)
// Provide convenient factory method
val article = Article("10.1038/nature01234", "a Title")
// and implementations for
assertThat("toString", article.toString, is("Article(10.1038/nature01234,a Title)"))
assertThat("hashCode", article.hashCode, equalTo( equalArticle.hashCode ))
assertTrue("equals", article == equalArticle)
Pattern matching
• match as switch on steroids• always results in a value• no “fall through”• MatchError thrown if no match• _ is default
Matching by constantdef fun(o: Any): Int = o match {
case "abc" => 1
case 1 => 2
case Nil => 0
}
Matching by type(no more instanceof and casting)
def size(o: Any): Int = o match {
case s: String => s.length
case l: List[_] => l.size
case _ => -1
}
assertThat(size("abc"), is(3))
assertThat(size(List(1, 2)), is(2))
Matching by tupledef fun(o: Any): Any = o match {
case (a: Int, b: Int) => a + b
}
Matching by constructorcase class Rectangle(a: Int, b: Int)
def identify(r: Rectangle): String = r match {
case Rectangle(2, 4) => "2x4"
case Rectangle(a, b) if a == b => "Square" // pattern guard
case Rectangle(a, b) => "Rectangle " + a + "x" + b
}
assertThat(identify(Rectangle(2, 4)), is("2x4"))
assertThat(identify(Rectangle(1, 1)), is("Square"))
assertThat(identify(Rectangle(1, 2)), is("Rectangle 1x2"))
(optional) Exception handling try {
val f = new FileInputStream("file.txt")
} catch {
case e: IOException => // handle the exception
}
but can be also just:
val f = new FileInputStream("file.txt")
Named and default parameterscase class Article(id: Int, doi: String, title:
String)
def make(id: Int = 1, doi: String = "a Doi", title: String = "a Title") = {
Article(id, doi, title)
}
Named and default parametersDefaulting omitted parameter(s)
val article = make(99, "10.1038/nature01234")
assertThat(article.id, is(99))
assertThat(article.doi, is("10.1038/nature01234"))
assertThat(article.title, is("a Title"))
Named and default parametersSetting parameter by name
val article = make(doi = "10.1038/nature01234")
assertThat(article.id, is(1)) // default
assertThat(article.doi, is("10.1038/nature01234"))
assertThat(article.title, is("a Title")) // default
CollectionsEasy construction
val aList = List(1, 2, 3)
val aMap = Map("A" -> 1, "B" -> 2, "C" -> 3)
val aSet = Set("a", "b", "c", "b", "a")
scala> val list = List(1) ::: List(2) // concatenate
list: List[Int] = List(1, 2)
scala> val list = List(1) ++ List(2) // concatenate
list: List[Int] = List(1, 2)
scala> val list = 1 :: List(2) // add element (as a first one)
list: List[Int] = List(1, 2)
CollectionsIterable - a base trait for iterable collections with ~100 functions
scala> val numbers = List(1, 2, 3, 4, 5)
scala> val negatives = numbers.map(e => -e)
negatives: List[Int] = List(-1, -2, -3, -4, -5)
scala> val even = numbers.filter(n => n % 2 == 0)
even: List[Int] = List(2, 4)
scala> val (even, odd) = numbers.partition(_ % 2 == 0)
even: List[Int] = List(2, 4)
odd: List[Int] = List(1, 3, 5)
Collectionsscala> val result = numbers.mkString(", ")
result: String = 1, 2, 3, 4, 5
scala> val result = numbers.count(_ > 2)
result: Int = 3
scala> numbers.foreach(print _)
12345
CollectionsExample for groupBy
scala> case class Article(journal: String, doi: String)
scala> val articles = List(Article("NATURE", "nature09733"), Article("NMAT", "nmat2937"), Article("NMAT", "nmat1807"), Article("NPHYS", "nphys1906"))
scala> val result = articles.groupBy(e => e.journal)
scala> val nmat = result("NMAT")
nmat: List[Article] = List(Article(NMAT,nmat2937), Article(NMAT,nmat1807))
scala> val nature = result("NATURE")
nature: List[Article] = List(Article(NATURE,nature09733))
scala> val nphys = result("NPHYS")
nphys: List[Article] = List(Article(NPHYS,nphys1906))
XML literals and XPath-like processing
// given
val xml = <article id="1">
<doi>10.1038/nature01234</doi>
<title>A Title</title>
</article>
// then
assertThat(xml \ "@id" text, is("1"))
assertThat(xml \"doi" text, is("10.1038/nature01234"))
Evaluating Scala code in XML literal
// given
val tstamp = System.currentTimeMillis
val xml = <timestamp>{tstamp}</timestamp>
// then
xml = <timestamp>1295726256063</timestamp>
Loading XML// given
val xml = XML.loadString("<code>ABC</code>")
// then
assertThat(xml, is(<code>ABC</code>))
Pattern matching on XMLval xml = <info>
<message>a message</message>
</info>
val result = xml match {
case <info>{_*}</info> => logInfo(xml)
case <error>{_*}</error> => logError(xml)
}
Concurrency – Actor model
• Asynchronous message passing between actors
• Messages are immutable• Synchronized mailbox• Mailbox is read with pattern matching• No shared state + No locks = Safe concurrency• Actors are lightweight → millions of them on
regular box
Type parameterizationclass A
class B extends A
class C extends B
def upperBound[T <: B](o: T) {}
upperBound(new A)
upperBound(new B)
upperBound(new C)
Type parameterizationdef getSize[T <: { def size: Int }](o: T): Int = {
o.size
}
getSize(List(1, 2, 3))
getSize(new StringBuilder("abc"))
getSize("abc") // String has length() not size()
Automatic Resource Managementuse of type parameterization and closure
def using[T <: {def close()}](resource: T)(block: T => Unit) = {
try {
block(resource)
} finally {
if (resource != null) {
try resource.close
catch {
case e: Exception =>
}
}
}
}
Automatic Resource Managementusing(stream) { // where stream is InputStream
res => { val data = res.read }
}
using(resultSet) { // where resultSet is ResultSet
res => {
val data = if (res.next) res.getString("column") else ""
}
}
ScalaTest BDD testing DSLclass StackSpec extends Spec with ShouldMatchers {
describe("A Stack") {
describe("(when empty)") {
val stack = new Stack[Int]
it("should be empty") {
stack should be ('empty)
}
it("should complain when popped") {
evaluating { stack.pop() } should produce [NoSuchElementException]
}
}
}
}
Tooling
• Plugins for Eclipse, NetBeans and IDEA• Support for (some) refactorings, debugging,
code completion, hints, syntax highlighting, navigation
• maven support (also mix Java/Scala projects)• sbt – simple build tool
Benefits
• Productivity• Functional aspects• XML• DSL• Concurrency
Quirks (functions)Way functions are declared determines how they can be used
def foo = 1
def bar() = 2
foo
foo()
bar
bar()
Warning!
There is a danger that once you start programming in Scala you may not want to
program in Java anymore!
ResourcesScala - http://www.scala-lang.org/
A Tour of Scala - http://www.scala-lang.org/node/104
Scala API - http://www.scala-lang.org/api/current/index.html
New features of Scala 2.8 - http://escalatesoft.com/screencasts
Scala for Java Refugeeshttp://www.codecommit.com/blog/scala/roundup-scala-for-java-refugees
AlBlue’s Blog - http://alblue.bandlem.com/search/label/scala
Scala Style Guide (good to learn about some Scala quirks) - http://davetron5000.github.com/scala-style/
Scaling Web Apps with Akka - http://maciejmatyjas.com/2010/07/scaling-web-apps-with-akka/
ResourcesProgramming in Scala - http://www.artima.com/shop/programming_in_scala
Programming Scala - http://programming-scala.labs.oreilly.com/
Seven Languages in Seven Weeks (chapter, comparison with Java and Ruby)http://pragprog.com/titles/btlang/seven-languages-in-seven-weeks
Programming Scala - http://pragprog.com/titles/vsscala/programming-scala
Scala in London
London Scala User Group http://www.meetup.com/london-scala/
regular meetups – recorded and available athttp://skillsmatter.com/go/scala
LSUG Scala Dojo
Thanks!
http://tomaszwrobel.com@twr_wrtp