advanced scala

53
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching Masterseminar Scala Fabian Becker FH Giessen-Friedberg 15. November 2010

Upload: fabian-becker

Post on 10-Nov-2014

1.005 views

Category:

Technology


0 download

DESCRIPTION

This presentation was part of a seminar in a masters course. It covers 3 chapters of Martin Odersky's Book "Programming in Scala" (Packages and Imports, Assertions and Unit testing, Case Classes and Pattern Matching).

TRANSCRIPT

Page 1: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Masterseminar Scala

Fabian Becker

FH Giessen-Friedberg

15. November 2010

Page 2: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Content

1 Packages & Imports

2 Assertions and Unit Testing

3 Case Classes and Pattern Matching

Page 3: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Packages

Packages

Important to minimize coupling

Scala code resides in Java global hierarchy of packages

Per default code is in the unnamed package

There are two ways to place code inside packages

Listing 1.1

package database.mysql

class Query

Page 4: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Packages

Listing 1.2

package database {

package mysql {

class Query

}

}

Listing 1.3

package database.mysql {

class Query

}

Page 5: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Packages

Packages

Packages in Scala truly nest

In Java packages are in a hierarchy they don’t nest

Naming a package in Java you always start at the root

Page 6: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Packages

Listing 1.4: Accessing packages

package game {

package ui {

class Player

}

package engine {

class Main {

// Java: new game.ui.Player()

val player = new ui.Player

}

}

}

Page 7: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Packages

Listing 1.5: More complex

package ui { class Player } // In ui.scala

package game { // In game.scala

package ui { class Player }

package engine {

package ui { class Player }

class Main {

val player = new ui.Player

val player2 = new game.ui.Player

val player3 = new _root_.ui.Player

}

}

}

Page 8: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Imports

Importing packages

Importing packages allows to directly access members in thatpackage

Scala imports are similar to the imports from Java

Imports can appear anywhere

Page 9: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Imports

Listing 1.6.1: Defining Enemies

package game

abstract class Enemy(

val name: String,

var lvl: Int

)

object Enemies {

object Dwarf extends Enemy("Gimli", 10)

object Orc extends Enemy("Bwarg", 5)

object Necromancer extends Enemy("Destructor", 99)

val col = List(Dwarf, Orc, Necromancer)

}

Page 10: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Imports

Listing 1.6.2: Importing Enemies

// Simple import for Enemy

import game.Enemy

// Import all members of game

import game._

// Import all members of Enemies

import game.Enemies._

Page 11: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Imports

Listing 1.7: Import in a function

def showEnemy(enemy: Enemy) {

import enemy._

println("Enemy - Name:"+name+" Level:"+lvl)

}

Imports

Imports can appear in functions

They import all members of a object

Page 12: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Selective Imports

Listing 1.8: Selective Imports

// Only import Dwarf and Orc

import game.Enemies.{Dwarf, Orc}

// Renaming an import

import game.Enemies.{Necromancer => Necro}

// Rename one, import the rest

import game.Enemies.{Orc => O, _}

// Exclude an import

import game.Enemies.{Orc => _, _}

Page 13: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Selective Imports

Selective Imports

Import: import game.Enemies.{Orc} ⇔ game.Enemies.Orc

Rename: 〈originalname〉 ⇒ 〈newname〉Hide Packages: 〈original − name〉 ⇒Catch All: importgame.Enemies.{ } ⇔ game.Enemies.

Page 14: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Implicit imports

Listing 1.9: Implicit imports

import java.lang._ // everything in the java.lang package

import scala._ // everything in the scala package

import Predef._ // everything in the Predef object

Implicit imports

Packages imported implicitly by Scala

scala. overshadows java.lang.

scala.StringBuilder ⇒ java.lang.StringBuilder

Page 15: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Access modifiers

Listing 1.10: Private modifiers

class Outer {

class Inner {

private def f() { println("f") }

class MostInner { f(); // OK }

}

(new Inner).f() // fails

}

Private

Scala: private members are only accessible inside the class orobject that contain their definition

Java allows outer classes to access private members of theirinner classes

Page 16: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Access modifiers

Protected

Java: protected members are accessible within the samepackage

Scala: Only from the object they are defined in and theirsubclasses

Public

There is no explicit modifier for public

All members not labeled private or protected are public

Accessable from anywhere

Page 17: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Access modifiers

Scope of protection

Scala allows to control access to a level X

private[X] and protected[X] allow access up to X

Listing 1.11: Example

package game {

package ui {

private[game] class Player {

private[this] var hp = 42

protected[ui] def getHP() { hp }

}

}

}

Page 18: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Assertions and Unit Testing

Testing Code

Assertions for run-time checking

Unit testing for development testing of small units

Page 19: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Assertions

Listing 2.1: Assert

def add1(x: Int) : Int = {

val z = x + 1

assert(z > x)

return z

}

println(add1(41))

Assertion methods

Method assert predefined in Predef

assert(condition)

assert(condition, explanations)

If the condition doesn’t hold, an AssertionError is thrown

Page 20: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Assertions

Listing 1.13: Ensuring

def lvlUp(e: Enemy): Enemy = {

if(e.lvl < 100) {

e.lvl = e.lvl + 1

} ensuring(e.lvl < 100)

e

}

Ensuring

Method assert predefined in Predef

assert(condition)

assert(condition, explanations)

If the condition doesn’t hold, an AssertionError is thrown

Page 21: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

ScalaTest

org.scalatest.Suite (http://www.scalatest.org/)

Can be executed on the Scala console: (newHitchhikerSuite).execute()

Listing 2.3: Simple testcase

import org.scalatest.Suite

class HitchhikerSuite extends Suite {

def testAnswerToLife() {

val answer = 42

assert(answer == 42)

}

}

Page 22: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

ScalaTest with Fun

ScalaTest allows different styles of testing

FunSuite is a trait that overrides execute and allows to definetests as function values

Naming the tests is easier (you can use Strings)

Listing 2.4: FunSuite

import org.scalatest.FunSuite

class HitchhikerSuite extends FunSuie {

test("The answer to life should be 42") {

val answer = 42

assert(answer == 42)

}

}

Page 23: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

ScalaTest - Informative testing

A normal assertion produces long error messages but do notshow why the match failed

ScalaTest defines the === operator for assert(), shows thevalues

Alternative: expect(value) { block }

Listing 2.5: Informative testing

assert(answer === 42)

// or

expect(42) {

answer

}

Page 24: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

ScalaTest - Catching Exceptions

Testing for Exceptions can be done with intercept()

Listing 2.6: Intercept

s = "hi"

intercept[IndexOutOfBoundsException] {

s.charAt(-1)

}

Page 25: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

JUnit

Most popular test framework for Java

Can easily be used from Scala

Does not natively support Scala’s assertion syntax

Listing 2.7: JUnit Testcase

import junit.framework.TestCase

import junit.framework.Assert.assertEquals

import junit.framework.Assert.fail

class MyTestCase extends TestCase {

def testAnswer() {

val answer = 42

assertEquals(42, answer)

}

}

Page 26: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

JUnit + Scala

ScalaTest defines JUnit4Suite (requires JUnit 4 of course)

JUnit4Suite extends TestCase

Listing 2.8: Using Scala syntax in JUnit

import org.scalatest.junit.JUnit4Suite

class AnswerSuite extends JUnit4Suite {

def testAnswerToLife() {

var answer = 42

assert(answer === 42)

}

}

Page 27: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

TestNG

TestNG framework inspired by JUnit/nUnit

Uses annotations

Listing 2.9: TestNG

import org.testng.annotations.Test

import org.testng.Assert.assertEquals

class AnswerTests {

@Test def verifyAnswerToLife() {

val answer = 42

assertEquals(answer, 42)

}

}

Page 28: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

TestNG + Scala

ScalaTest defines trait TestNGSuite

TestNGWrapperSuite enables TestNG to test Scala while thetests are written in Java

Listing 2.10: TestNG + Scala

import org.scalatest.testng.TestNGSuite

import org.testng.annotations.Test

class AnswerSuite extends TestNGSuite {

@Test def verifyAnswerToLife() {

val answer = 42

expect(42) { answer }

assert(answer === 42)

}

}

Page 29: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

Listing 2.11: TestNG + Scala

import org.scalatest.Spec

class AnswerSpec extends Spec {

"The Answer" -- {

"should be an integer" - {

val answer = 42

assert(answer.isInstanceOf[Int] == true)

}

"should be 42" - {

val answer = 42

assert(answer === 42)

}

}

}

Page 30: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Unit testing

Tests as specification

Behavior-driven-development (BDD) lays emphasis onhuman-readable testing

ScalaTest includes a trait Spec

Spec contains describers and specifiers

Each specifier will be run as a seperate ScalaTest

Output (new AnswerSpec).execute()

The answer- should be an integer- should be 42

Page 31: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Case Classes and Pattern Matching

Tests as specification

Case Classes

Pattern Matching

Page 32: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Case classes

Listing 3.1: Definitions

abstract class Expr

case class Var(name: String) extends Expr

case class Number(num: Double) extends Expr

case class UnOp(operator: String, arg: Expr) extends Expr

case class BinOp(operator: String,left: Expr, right:

Expr) extends Expr

The case modifier

Example from the book

The case modifier adds syntactic conveniences

Factory method: val v = Val(”x”)

Page 33: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Case classes

Listing 3.2: Nesting

val op = BinOp("+", Number(42), Var("x"))

The case modifier

Allows to ommit the new

All constructor parameters implicitly get a val prefix

toString, equals, hashCode are automagically added

Page 34: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Listing 3.3: Matching

def simplifyTop(expr: Expr): Expr = expr match {

case UnOp("-",UnOp("-",e)) => e // Double negation

case BinOp("+", e, Number(0)) => e // Adding zero

case BinOp("*", e, Number(1)) => e // Multiplying by one

case _ => expr

}

Matching

Similar to Javas switch

Syntax: selector match {alternatives}match is an expression and results in a value

Page 35: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Patterns

e, variable pattern

, wildcard pattern, drops the value

UnOp(“-“, e), constructor pattern, matches values of typeUnOp with - as first param.

If no pattern matches a MatchError is thrown.

Listing 3.4: Matching

expr match {

case BinOp(_, _, _) => println(expr +"is a binary

operation")

case _ => println("It’s something else")

}

Page 36: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Constant patterns

Any literal (String, Boolean, Integer) can be used as a pattern

Literals match themselves

Listing 3.5: Constant patterns

def inspect(x: Any) = x match {

case 12 => "twelve"

case Nil => "nothing"

case false => "negative"

case _ => "Cant’t say"

}

Page 37: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Variable patterns

Variable pattern matches like a wildcard, but the result getsassigned to the variable

Listing 3.6: Variable patterns

expr match {

case 12 => "Not right"

case number => "This is a number " + number

}

Page 38: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

How does Scala tell them apart?

Scala uses a lexical rule to differ between variable andconstant patterns

Lowercase identifiers are treated as variable

Uppercase identifiers are assumed to be a constant

Exception to the rule?

true and false

Page 39: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Constructor patterns

Check against a constructor and it’s parameters

Object gets inspected

Contents of the object are checked aswell

Listing 3.7: Constructor pattern

expr match {

case BinOp("-", m, Number(12)) => println(m + " - 12")

case _ =>

}

Page 40: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Sequence patterns

Match against sequence types like List, Array, ...

Allows to specify the number of elements in the pattern

⇔ Wildcard, * ⇔ 0..n elements

Listing 3.8: Sequence pattern with fixed length

expr match {

case List(n, _, _) => println("Top-Element: " + n)

case List(n, _*) => println("Top-Element: " + n)

case _ =>

}

Page 41: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Tuple patterns

A pattern like (x, y) matches a 2-tuple

Listing 3.9: Tuple pattern

expr match {

case (x, y) => println("Matched a 2-tuple")

case _ =>

}

Page 42: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Typed patterns

Alternative for type checks and type casts

Type check: expr.instanceOf[String]

Type cast: expr.asInstanceOf[String]

Listing 3.10: Typed pattern

def getSize(e: Any) = e match {

case s: String => s.length

case m: Map[_,_] => m.size

case _ => -1

}

Page 43: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Type erasure

Matching a Map with special element types doesn’t work!

Scala/Java use the erasure model for generics

No information about type arguments is maintained atrun-time

Arrays are an exception, they are handled differently

Listing 3.11: Type erasure - Matches all Maps!

expr match {

case m: Map[Int, String] => println("String-Int Map")

case _ =>

}

Page 44: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Variable binding

Perform the normal match but bind the match to a variable

The @ sign binds a variable to a match

Listing 3.12: Binding a variable to a match

expr match {

case UnOp("abs", e @ UnOp("abs", _)) => e

case _ =>

}

Page 45: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Pattern guards

Sometimes a match can have pre-conditions

The conditions are “guarding“ the match

Listing 3.13: Match with pre-condition

expr match {

case BinOp("/", _, y) if y == 0 => println("Nominator

can’t be 0")

case _ =>

}

Page 46: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Pattern overlaps

Patterns are tried in the order they’re written

Changing the order of cases can change behaviour

Listing 3.14: Simple pattern overlap

expr match {

case BinOp("+", e, Number(10)) => println("BinOp + 10")

case BinOp("-", e, Number(10)) => println("BinOp - 10")

case BinOp(op, e, Number(10)) => println("BinOp "+op+"

10")

case _ => expr

}

Page 47: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Sealed classes

When you match using patterns, how do you make sure youmatched everything?

sealed classes allow the compiler to know all possible cases

A sealed class can only have subclasses in the same file

The compiler can then generate warnings (match notexhaustive)

Listing 3.15: Sealed class

sealed abstract class Expr

case class Var(name: String) extends Expr

case class Number(num: Double) extends Expr

Page 48: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Sealed classes

If you leave out an option a warning is thrown: “warning:match is not exhaustive! missing combination Type“

Listing 3.16: Match on a sealed class

expr match {

case Var(x) => "Variable " + x

}

Page 49: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Sealed classes

Warning can be avoided by doing a wildcard match

Suppressed using an annotation (will be covered in Chapter25)

Usefulness?

Why make the class sealed in the first place?

Listing 3.17: Match on a sealed class

(expr: @unchecked) match {

case Var(_) => "a variable"

}

Page 50: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

The Option type

Standard type for optional values

Can be Some(x) or None

Is returned by HashMap, Map, ..

None is the Scala way of returning null

Listing 3.18: Unpacking an option

val map = Map(1 -> "a", 2 -> "b", 3 -> "c")

(map get 1) match {

case Some(s) => println(s)

case None => println("Not there")

}

Page 51: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Patterns in other places

Patterns can be used almost anywhere

Listing 3.19: Other uses of patterns

val (a, b) = (1, 2)

for((num, letter) <- Map(1->"a", 2->"b"))

println("Num:"+num+" Letter:"+letter)

Page 52: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Case sequences as partial functions

Case sequences can be used partial functions

Partial functions throw a RuntimeException if they are appliedagainst a value they don’t support

Compiler can throw warnings

Listing 3.20: Case sequence as partial function

val myMatch: Option[Int] => String = {

case Some(x) => "Value: " + x

case None => "Nothing there"

}

myMatch(Some(12))

// => Value: 12

Page 53: Advanced Scala

Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching

Pattern matching

Case sequences as partial functions

By telling the compiler that you use a partial function you cancheck whether it’s possible to apply a value

Listing 3.21:

val listMatch: PartialFunction[List[Int], Int] = {

case x :: y :: _ => y

}

// Can we apply the List?

listMatch.isDefinedAt(List(1,2,3))

// => true