(how) can we benefit from adopting scala?

Post on 10-May-2015

1.595 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

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

top related