scala: object-oriented meets functional, by iulian dragos

Post on 01-Jul-2015

361 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

A presentation from Iulian Dragos of Typesafe that gives an overview of the Scala programming language. The presentation was given at a Functional Angle conference in Timisoara, Romania sponsored by 3Pillar. Iulian Dragos has been working on Scala since 2004. He currently works for Typesafe, a start-up that was co-founded by Scala’s creator, Martin Odersky.

TRANSCRIPT

Scala: Object-Oriented meets Functional

An overview of the Scala programming language

Iulian Dragos

1

What is Scala?

•A programming language running on the JVM (also a JS backend)

•Statically typed, combines object-orientation and functional programming

•Concise

•Fully interoperable with Java

•As fast as Java

2

History

•“Make Java better”

•1997 - Pizza and GJ (Odersky, Wadler)

•2000 - Java 5 (generics)

•“Make a better Java”

•first release 2004

•adoption begins 2007

•first Scala conference 20083

Scala drives its social graph service: 380-400 M transactions/day

Migrated core messaging service from Ruby to sustain 1000x growth

Location-based social network. All written in Scala (>5M users).

Approved for general production use

5

Philosophy

• “Growable language” (Guy Steele)

• embed DSLs/add new types

• Scalable language

• same concepts in small and large applications

• Deep rather than broad

• focus on abstraction and composition

6

public class Person { public final String name; public final int age; Person(String name, int age) { this.name = name; this.age = age; }}

class Person(val name: String, val age: Int)

Java

Scala

7

import java.util.ArrayList;...Person[] people;Person[] minors;Person[] adults;{ ArrayList<Person> minorsList = new ArrayList<Person>(); ArrayList<Person> adultsList = new ArrayList<Person>(); for (int i = 0; i < people.length; i++) (people[i].age < 18 ? minorsList : adultsList) .add(people[i]); minors = minorsList.toArray(people); adults = adultsList.toArray(people);}

val people: Array[Person]val (minors, adults) = people partition (_.age < 18)

A tuple in a pattern match

Infix method call

Anonymous function8

Overview

•Scala as an Object-Oriented Language

•Scala as a Functional Programming Language

•Scala as a host language for DSLs

9

Scala Basics

10

Running Scala

•Compiled to Java bytecode

•Read-Eval-Print-Loop (REPL)

•fast turnaround

•easy experimentation/testing

11

•Every value is an object

•Everything is an expression (evaluates to a value)

•Every operation is a method call

•What about primitives?

The bottom line

12

val x: Int = 10val y = x + 10

same as x.+(10)

type can be inferred

13

final class Int { def +(y: Int): Int = <native> def -(y: Int): Int = <native> def *(y: Int): Int = <native> def /(y: Int): Int = <native> ...}

The compiler treats all primitives as if they were instances of such classes (but uses primitive values for performance)

14

class Complex(val re: Int, val im: Int) { def +(that: Complex) = new Complex(this.re + that.re, this.im + that.im) // .. override def toString = "%d + %di".format(re, im)}

15

scala> val c = new Complex(1, 2)c: test.Complex = 1 + 2i

scala> val c1 = new Complex(2, 2)c1: test.Complex = 2 + 2i

scala> c + c1res0: test.Complex = 3 + 4i

16

Everything is an expression

•No statements

•Reduces the need for return and side-effects

17

def max(x: Int, y: Int) = if (x > y) x else y

scala> val t = { | x = x + 10 | x - 1 | }t: Int = 9

no return statement

return type inferred to Int

blocks evaluate to last expression

18

What about println?

scala> val x = println("hello, world")hello, worldx: Unit = ()

The Unit type is like void in Java: no interesting value

19

Classes and Traits

20

Classes

•Similar to Java classes

•have fields, methods, parameters and types

•every member can be overridden

•except classes (no virtual classes)

•any member can be abstract

21

abstract class Node[T](val v: T, next: Node[T]) extends List(v, next) { val cache: Int def first: T def children = List(next) override def toString = "Node " + v}

22

explicit abstract class parameters/fields

super ctor call

When compiled, this class will look and behave exactly like the obvious translation to a Java class. No glue code necessary.

Traits

•Like Java interfaces, but in addition

•allow concrete methods, fields, types

•Like Scala classes, but without constructor parameters

•Allow (a form of) multiple inheritance

•mix-in composition

23

trait Ordered[A] extends java.lang.Comparable[A] { def compare(that: A): Int

def < (that: A): Boolean = (this compare that) < 0 def > (that: A): Boolean = (this compare that) > 0 def <= (that: A): Boolean = (this compare that) <= 0 def >= (that: A): Boolean = (this compare that) >= 0}

type parameter can extend Java classes

classes mixing in Ordered will get all these methods ‘for free’

class Complex extends Ordered[Complex] { def compare(that: Complex): Int = ...}

24

if (c1 > c2) c1 else c2

val xs: List[Complex]xs.sorted

25

Mix-in composition

26

Mix-in example: Cells

•model mutable cells that contain a single value of some arbitrary type.

•Additionally, define logging and undoable variants.

27

A class for generic cells

class Cell[T](val init: T) { private var v = init

def get(): T = v def set(v1: T): Unit = { v = v1 }

override def toString: String = "Cell("+ v +")"}

make init available as a field

mutable field

28

trait UndoableCell[T] extends Cell[T] { import mutable.ArrayStack private var hist = new ArrayStack[T]()

override def set(v1: T): Unit = { hist.push(super.get()) super.set(v1) }

def undo(): Unit = super.set(hist pop)}

A trait for undoable cells

29

trait LoggingCell[T] extends Cell[T] { override def get(): T = { println("getting "+ this) super.get() }

override def set(v1: T): Unit = { println("setting "+ this +" to "+ v1) super.set(v1) }}

A trait for logging cells

30

new Cell(0)

new Cell(0) with LoggingCell[Int]

new Cell(0) with LoggingCell[Int] with UndoableCell[Int]

new Cell(0) with UndoableCell[Int] with LoggingCell[Int]

Mix-in composition

cell with logging

cell with logging and undoing

cell with undoing and logging

31

Mix-in composition

•traits can be stacked

•the call to super is dynamically bound

•super calls the ‘preceding’ implementation in the instantiated object

32

Late binding for super

33

Cell

myCell

LoggingUndo

Modules

•Scala uses objects and traits for module composition

•Composition through mixins

•Modules may require another module

34

Customers and DB

•Module Accounting

•requires a logger, a customer service

•Module Customers

•requires a logger, provides the customer service

35

trait Accounting { this: Customers => val logger: Logger customers.getCustomers // ..}

trait Customers { val logger: Logger val customers: CustomerService class CustomerService { def getCustomers: Unit = () //... }}

requires module Customers

36

object App extends Accounting with Customers { val logger = // logback val customers = new CustomerService}

The application uses (some) concrete implementations of Accounting and CustomerService.

37

object TestApp extends Accounting with Customers { val logger = // println based val customers = new MockCustomerService}

The test environment uses a mock CustomerService.

This gives dependency injection in the language!

38

39

Summary•Basic O-O features

•classes and traits

•type inference

•mix-in composition

•Advanced type system

•modules and dependencies

•dynamic binding of super

Functional Programming

40

What is FP?

•Use of functions (in the mathematical sense)

•referential transparency (no side-effects)

•Immutable objects

•Functions are values

41

Scala as FPL

•function literals and closures

•use val instead of var

•immutable collections in the standard library

•opt-in lazy evaluation

•curry-ing (definition site)

42

Function literals

scala> val succ = (x: Int) => x + 1succ: (Int) => Int = <function1>

scala> succ(1)res3: Int = 2

43

Equivalent forms

•x => x + 1 (infer type)

•1 + (partial application)

•_ + 1 (placeholder notation)

•compare _ (eta expansion)

44

Higher-Order Functions

•functions that take or return functions

•almost eliminate the need for loops over collections

45

val xs = List(1, 2, 3, 4, 5)xs.foreach(println)

scala> xs.forall(_ < 10)res5: Boolean = true

scala> xs.map(_ * 2)res6: List[Int] = List(2, 4, 6, 8, 10)

def findBelow(limit: Int) = persons.filter(_.age < limit)

lexical scoping: functions may refer and even modify anything in scope

46

Everything is an object

•Closures are objects as well

•(compiler generated) instances of trait Function1[A, B]

trait Function1[R, A] { def apply(x: A): R}

47

(More) sugar

•..but why can I call succ(10)?

•f(args) is desugared to f.apply(args)

•you can define your own ‘apply’ methods

•..can I subclass FunctionN?

48

Library

•Yes! Lots of collections are functions

•Sequences are Int => T

•Sets are T => Boolean

•Maps are K => V

49

Standard Library

50

Collections

•Generic (List[T], Map[K, V])

•Mutable and immutable implementations (default is immutable)

•Uniform (same method protocol for all collections)

•Uniform Return Principle

•No compiler magic!

51

Example: Mapsscala> val capitals = Map("France" -> "Paris", "Switzerland" -> "Bern", "Sweden" -> "Stockholm")capitals: immutable.Map[String,String] = Map(France -> Paris, Switzerland -> Bern, Sweden -> Stockholm)

scala> capitals("France")res7: java.lang.String = Paris

scala> capitals + ("Romania" -> "Bucharest")res8: immutable.Map[String,String] = Map(France -> Paris, Switzerland -> Bern, Sweden -> Stockholm, Romania -> Bucharest)

scala> capitalsres9: immutable.Map[String,String] = Map(France -> Paris, Switzerland -> Bern, Sweden -> Stockholm)

scala> capitals.filter(_._2 == "Paris")res10: immutable.Map[String,String] = Map(France -> Paris)

52

For-comprehensions

•More general than for-loops

•Used to iterate, filter and generate new collections

53

For-comprehensions

for (p <- persons; pr <- p.projects; if pr.overdue) yield p.name

may have any number of generators

guard construct a new collection of the same type, element by element

p is in scope for other generators

res10: List[String] = List(“John”, “Harry”)54

For-comprehensions

•Desugared to calls to filter, map, flatMap

•..any class implementing those methods

•Could be used to query a database

•implement filter, map, flatmap to generate and forward SQL

•Slick, ScalaQuery, Squeryl

55

Pattern matching

56

Pattern matching

•A way to deconstruct structured data

•A powerful switch statement (expression, really)

57

Pattern matchingabstract class Expr

case class Literal(n: Int) extends Expr

case class Add(left: Expr, right: Expr) extends Expr

case class Mul(left: Expr, right: Expr) extends Expr

the case modifier enables these classes to participate in pattern matching

58

Pattern matchingdef opt(e: Expr) = e match { case Add(l, Literal(0)) => l

case Mul(l, Literal(1)) => l

case _ => e}

patterns can be nested

literals match only their value

default case

59

Patterns everywhere

•Great way to process XML, JSON, etc.

•exception handlers are also patterns!

•..and also declarations

60

61

try { //... } catch { case EvalError(line, msg) => //.. case e: IndexOutOfBoundsException => //.. }

Embedding DSLs

62

Implicit conversions

•We can “grow” the language with Complex numbers

•c + 1 works, but what about 1 + c?

63

Implicit conversions

•Allow user-defined conversions

•When the type does not have a member

•Find a method marked implicit that takes the original type to one that has it

•Call the method implicitly

•It’s an error to have more than one applicable conversions

64

implicit def intIsComplex(n: Int) = new RichInt(n)

class RichInt(n: Int) { def + (other: Complex) = new Complex(n) + other}

1 + c intIsComplex(1).+(c)65

implicit class RichInt(n: Int) { def + (other: Complex) = new Complex(n) + other}

66

Also a great way to adapt existing libraries

67

implicit class RichArray[T](as: Array[T]) { def foreach(f: T => Unit) = ??? def map[U](f: T => U) = ??? def filter(f: T => Boolean) = ??? // ... }

Scala arrays are Java arrays, but can still have the same API

Actor-based concurrency

•Threads and locks are error-prone

•(in fact, mutable shared data)

•Alternative: Message-passing with actors

•(no sharing)

•Implemented entirely in the library

•(3 other implementations out there)

68

Actors in Scala

actor ! msg

receive { case Ping(a1) => a1 ! msg case Pong(_) => ..}

asynchronous message send

Normal pattern match translated to PartialFunction

method call

69

What else?

70

Lots

•Parallel collections

•Staging in the

librar

y!

•Call-by-name parameters

•Specialization

71

Where to start•Pick one of the books

•Scala for the Impatient (Addison-Wesley)

•Atomic Scala (Bruce Eckel)

•Scala for Java refugees (blog)

72

Thank you!

73

top related