functional scala ii (in practice)
TRANSCRIPT
Mario Gleichmann JUG Frankfurt / Main
Functional Scaλa
… in Practice
20.10.2010
Java User Group Frankfurt / MainMario Gleichmann
Mario Gleichmann JUG Frankfurt / Main2
Introduction
Mario Gleichmann
site: www.mg-informatik.de
blog: 'brain driven development'
gleichmann.wordpress.com
mail: [email protected]
Mario Gleichmann JUG Frankfurt / Main3
Summary
Mario Gleichmann JUG Frankfurt / Main4
What is Functional Programming ?
Functional Style
val sum = fold( 1 to 10, add )
Expression based
Computation method is function application
fcn
Mario Gleichmann JUG Frankfurt / Main5
What makes a Function ?
val add = ( x :Int, y :Int ) => x + y
Function Types
Type of add : ( Int , Int ) => Int
Obj
Mario Gleichmann JUG Frankfurt / Main6
Closures
var limit = 18 val isAdult = ( age :Int ) => age >= limit
'Closed term'
appl
'closing over'
Mario Gleichmann JUG Frankfurt / Main7
Algebraic Datatypes
data Tree = Empty | Leaf Int | Node Int Tree Tree
abstract case class Tree() case object Empty extends Tree case class Leaf( value: Int ) extends Tree
case class Node( value: Int, left :Tree, right: Tree ) extends Tree
Example - Tree
insrt
Mario Gleichmann JUG Frankfurt / Main8
Pattern Matching
val depth :Tree => Int = _ match { case Empty => 0 case Leaf( _ ) => 1 case Node( _, left, right ) => 1 + max( depth( left ), depth( right ) )}
Prt isb
Mario Gleichmann JUG Frankfurt / Main9
Partial Functions
type =>?[-A, +B] = PartialFunction[A, B]
val publisherRegionDE : Isbn =>? String = { case Isbn( 3, pubNr, _ ) => "D" + ( pubNr.toString charAt 0 )}
…
publisherRegionDE.isDefinedAt( Isbn( 0, 677873, 8823 ) ) )
>> false
chn
Mario Gleichmann JUG Frankfurt / Main10
Comprehensions
val factors = ( n :Int ) => for( x <- ( 1 to n ) if n % x == 0 ) yield x ...
val prime = ( n :Int ) => factors( n ).toList == List( 1, n )
…
val primes = (n :Int) => for( x <- (1 to n) if prime( x ) ) yield x
Hgh rdr Fcn
Mario Gleichmann JUG Frankfurt / Main11
Higher Order Functions
val filter = ( predicate: Int => Boolean , xs :List[Int] ) => {
for( x <- xs; if predicate( x ) ) yield x }
Fcn Tp
Mario Gleichmann JUG Frankfurt / Main12
Lambda Expressions
filter( num => num % 2 == 0, List( 1, 2, 3, 4, 5, 6 ) )
…
filter( _ > 0, List( -1, 5, 0, -3, 7 ) )
fst arg
Mario Gleichmann JUG Frankfurt / Main13
Partial Application
Mario Gleichmann JUG Frankfurt / Main14
Partial Application
val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 )
Type of isPrime: Int => Boolean
def prms wthn
Mario Gleichmann JUG Frankfurt / Main15
Partial Application
val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 )
val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs )
Delegation
Type of filter: ( Int => Boolean, List[Int] ) => List[Int]
fxd var
Mario Gleichmann JUG Frankfurt / Main16
Partial Application
val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 )
val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs )
Delegation
variablefixed
appl nappl
Mario Gleichmann JUG Frankfurt / Main17
Partial Application
val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 )
val primesWithin = filter( isPrime, _ :List[Int] )
New from Old ...
unappliedapplied
Fcn Tp
Mario Gleichmann JUG Frankfurt / Main18
Partial Application
val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 )
val primesWithin = filter( isPrime, _ :List[Int] )
New from Old ...
Type of primesWithin: List[Int] => List[Int]
Crrng
Mario Gleichmann JUG Frankfurt / Main19
Currying
Mario Gleichmann JUG Frankfurt / Main20
Currying
val filter = ( pred: Int => Boolean , xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Type of filter: ( Int => Boolean , List[Int] ) => List[Int]
Crrd fcn
Mario Gleichmann JUG Frankfurt / Main21
Currying
'Higher Order Lambda Closures' ...
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Crrd fcn Tp
Mario Gleichmann JUG Frankfurt / Main22
Currying
'Higher Order Lambda Closures' ...
Type of filter: ( Int => Boolean ) => ( List[Int] ) => List[Int]
sngl arg
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Mario Gleichmann JUG Frankfurt / Main23
Currying
Curried FunctionCurried Function
'Higher Order Lambda Closures' ...
xpln
Type of filter: ( Int => Boolean ) => ( List[Int] ) => List[Int]
sngl arg
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Mario Gleichmann JUG Frankfurt / Main24
Currying
'Higher Order Lambda Closures' ...
A function ... accepting one (Function) Arg
... resulting in another function
lmbd xpr
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Type of filter: ( Int => Boolean ) => ( List[Int] ) => List[Int]
Mario Gleichmann JUG Frankfurt / Main25
Currying
'Higher Order Lambda Closures' ...
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Type of filter: ( Int => Boolean ) => List[Int] => List[Int]
... is defined as a Lambda expession
cls ovr
Mario Gleichmann JUG Frankfurt / Main26
Currying
'Higher Order Lambda Closures' ...
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
Type of filter: ( Int => Boolean ) => List[Int] => List[Int]
... closes over to Arguments (Scope) of surrounding Function 'filter'
Smp prms wthn
Mario Gleichmann JUG Frankfurt / Main27
Example 1 – Extract primes
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
val primesWithin = filter( isPrime )
Fcn Tp
Mario Gleichmann JUG Frankfurt / Main28
Example 1 – Extract primes
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
val primesWithin = filter( isPrime )
Type of primesWithin: List[Int] => List[Int]
appl
Mario Gleichmann JUG Frankfurt / Main29
Example 1 – Extract primes
val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => {
for( x <- xs; if pred( x ) ) yield x }
val primesWithin = filter( isPrime )
...primesWithin( List( 3, 4, 5, 6, 7 ) ) >> List( 3, 5, 7 )
Smp rc mgmt
Mario Gleichmann JUG Frankfurt / Main30
Example 2 – Ressource Management
val initQuery = (dataSource :DataSource) => (query :String) => ( extractor :ResultSet => Unit ) => { try{ val conn = dataSource.getConnection val stmt = conn.createStatement val resultSet = stmt.executeQuery( query ) extractor( resultSet ) } finally{ try{ if( conn != null ) conn.close } finally{ if( stmt != null ) stmt.close } } }
appl
Mario Gleichmann JUG Frankfurt / Main31
Example 2 – Ressource Management
val dataSource :DataSource = ...;
...val query = initQuery( dataSource )
…
query( "select * from User where age > 18" ) { result :ResultSet =>
while( result.next ) { .... } }
…
query( "select * from Account where balance < 1000" ) { result :ResultSet =>
while( result.next ) { .... } }
smmy
Mario Gleichmann JUG Frankfurt / Main32
Combinators
Mario Gleichmann JUG Frankfurt / Main33
Combinators
val power : Int => Int => Int =
( base :Int ) => ( exp :Int ) =>
if( exp == 1 ) base else base * power( base )( exp - 1 )
...
val square = power( _ :Int )( 2 )
Mario Gleichmann JUG Frankfurt / Main34
Combinators
val power : Int => Int => Int =
( base :Int ) => ( exp :Int ) =>
if( exp == 1 ) base else base * power( base )( exp - 1 )
...
val square = power( _ :Int )( 2 )
Type of square : Int => Int
Mario Gleichmann JUG Frankfurt / Main35
Combinators
val power : Int => Int => Int =
( base :Int ) => ( exp :Int ) =>
if( exp == 1 ) base else base * power( base )( exp - 1 )
...
val square = power( _ :Int )( 2 )
Partial Application 'in the middle'
Mario Gleichmann JUG Frankfurt / Main36
Combinators
def flipArgs [A, B, C] ( f: A => B => C ) : B => A => C = ( x: B ) => ( y: A ) => f (y) (x)
Transforms to another Function, expecting arguments in reversed order
Transforms to another Function, expecting arguments in reversed order
Mario Gleichmann JUG Frankfurt / Main37
Combinators
def flipArgs [A, B, C] ( f: A => B => C ) : B => A => C = ( x: B ) => ( y: A ) => f (y) (x)
…
val square = flipArgs( power )( 2 )
… now just exploit Currying
Mario Gleichmann JUG Frankfurt / Main38
Combinators
Function Composition
f :: B => C
g :: A => B
x ∈ A
( f o g )( x ) = f ( g ( x ) )
( f o g ) :: A => C
Mario Gleichmann JUG Frankfurt / Main39
Combinators : Function Composition
def o [A,B,C] ( f: B => C ) ( g: A => B ) : A => C =
( x :A ) => f( g( x ) )
...val mult = ( x :Int ) => ( y: Int ) => x * y
val add = ( x :Int ) => ( y: Int ) => x + y
...val doubleSucc = o ( mult(2) ) ( add(1) ) Type of doubleSucc : Int => Int
Mario Gleichmann JUG Frankfurt / Main40
Combinators : Function Composition
def o [A,B,C] ( f: B => C ) ( g: A => B ) : A => C =
( x :A ) => f( g( x ) )
...val mult = ( x :Int ) => ( y: Int ) => x * y
val add = ( x :Int ) => ( y: Int ) => x + y
...val doubleSucc = o ( mult(2) ) ( add(1) ) doubleSucc( 7 )
>> 16
Mario Gleichmann JUG Frankfurt / Main41
Combinators : Function Composition
def o [A,B,C] ( f: B => C ) ( g: A => B ) : A => C =
( x :A ) => f( g( x ) )
...val mult = ( x :Int ) => ( y: Int ) => x * y
val add = ( x :Int ) => ( y: Int ) => x + y
...val doubleSucc = o ( mult(2) ) ( add(1) ) doubleSucc( 7 )
>> 16Postfix ...
Mario Gleichmann JUG Frankfurt / Main42
Combinators : Function Composition
class RichFunc [B, C] ( f: B => C ) {
def o[A] ( g: A => B ) = (x :A) => f( g( x ) )}
implicit def toRichFunc[B,C]( f: B => C ) = new RichFunc( f )
...val doubleSucc = mult(2) o add(1) doubleSucc( 7 )
>> 16
Infix !!!
Mario Gleichmann JUG Frankfurt / Main43
Combinators : Function Composition
class RichFunc [B, C] ( f: B => C ) {
def o[A] ( g: A => B ) = (x :A) => f( g( x ) )}
implicit def toRichFunc[B,C]( f: B => C ) = new RichFunc( f )
...val doubleSucc = mult(2) o add(1) doubleSucc( 7 )
>> 16
'Point free style' of function definition
Mario Gleichmann JUG Frankfurt / Main44
Example: Word Counter
Count all even words within a sentence:
“Hello World“ → 0
“The fox jumps over the lazy dog“ → 2
"This sentence contains five even words" → 5
Mario Gleichmann JUG Frankfurt / Main45
Example: Word Counter
Count all even words within a sentence:
“Hello World“ → 0
“The fox jumps over the lazy dog“ → 2
"This sentence contains five even words" → 5
Basic Functions:
parse Words → Word to length → filter even → count
Mario Gleichmann JUG Frankfurt / Main46
Example: Word Counter
val words = ( sentence : String ) => sentence.split( " " ).toList val length = ( ws :List[String] ) => for( w <- ws ) yield w.length
val filter = ( predicate: Int => Boolean ) => ( xs :List[Int] ) => ...
val even = ( i :Int ) => i % 2 == 0
val size = ( xs :List[ _ ] ) => xs.size
Basic Functions
Mario Gleichmann JUG Frankfurt / Main47
Example: Word Counter
val words = ( sentence : String ) => sentence.split( " " ).toList val length = ( ws :List[String] ) => for( w <- ws ) yield w.length
val filter = ( predicate: Int => Boolean ) => ( xs :List[Int] ) => ...
val even = ( i :Int ) => i % 2 == 0
val size = ( xs :List[ _ ] ) => xs.size
val evenWordCount = size o filter( even ) o length o words
Type of evenWordCount : String => Int
Mario Gleichmann JUG Frankfurt / Main48
Example: Word Counter
val words = ( sentence : String ) => sentence.split( " " ).toList val length = ( ws :List[String] ) => for( w <- ws ) yield w.length
val filter = ( predicate: Int => Boolean ) => ( xs :List[Int] ) => ...
val even = ( i :Int ) => i % 2 == 0
val size = ( xs :List[ _ ] ) => xs.size
val evenWordCount = size o filter( even ) o length o words
val evenWordCount = (sentence :String ) =>
size ( filter( even ) ( length( words( sentence ) ) ) )
vs.
Mario Gleichmann JUG Frankfurt / Main49
Example: Word Counter
val words = ( sentence : String ) => sentence.split( " " ).toList val length = ( ws :List[String] ) => for( w <- ws ) yield w.length
val filter = ( predicate: Int => Boolean ) => ( xs :List[Int] ) => ...
val even = ( i :Int ) => i % 2 == 0
val size = ( xs :List[ _ ] ) => xs.size
val evenWordCount = size o filter( even ) o length o words
evenWordCount( "this sentence contains five even words" )
>> 5
Mario Gleichmann JUG Frankfurt / Main50
Fundamental Functions
Mario Gleichmann JUG Frankfurt / Main51
Fundamental Functions
def sum ( list : List[Int] ) : Int = list match { case Nil => 0 case x :: xs => x + sum( xs )}
Mario Gleichmann JUG Frankfurt / Main52
Fundamental Functions
def product ( list : List[Int] ) : Int = list match { case Nil => 1 case x :: xs => x * product( xs )}
Mario Gleichmann JUG Frankfurt / Main53
Fundamental Functions
def allTrue ( ys : List[Boolean] ) : Boolean = ys match { case Nil => true case x :: xs => x and allTrue( xs ) }
Mario Gleichmann JUG Frankfurt / Main54
Fundamental Functions : reduce
def reduce ( list : List[ T ] ) : R = list match { case Nil => ? case x :: xs => x ʘ reduce( xs ) }
Mario Gleichmann JUG Frankfurt / Main55
Fundamental Functions : reduce
def reduce ( list : List[ T ] ) : R = list match { case Nil => ? case x :: xs => x ʘ reduce( xs ) }
'unit'
'subsuming operation'
Mario Gleichmann JUG Frankfurt / Main56
Fundamental Functions : reduce
def reduce[T,R] ( fun : T => R => R ) ( unit : R ) ( list : List[T] ) : R = list match { case Nil => unit case x :: xs => fun ( x ) ( reduce( fun )( unit )( xs ) ) }
Mario Gleichmann JUG Frankfurt / Main57
Fundamental Functions : reduce
def reduce[T,R] ( fun : T => R => R ) ( unit : R ) ( list : List[T] ) : R = list match { case Nil => unit case x :: xs => fun ( x ) ( reduce( fun )( unit )( xs ) ) }
reduce ʘ unit t1 :: t2 :: t3 :: t4 :: Nil => t1 ʘ ( t2 ʘ ( t3 ʘ ( t4 ʘ unit ) ) )
Mario Gleichmann JUG Frankfurt / Main58
Fundamental Functions : reduce
val add = (x : Int) => (y : Int) => x + y val mult = (x : Int) => (y : Int) => x * y ... val sum = reduce ( add ) ( 0 ) _ val product = reduce ( mult ) ( 1 ) _
... sum( List( 1, 2, 3, 4 ) )
product( List( 1, 2, 3, 4 ) )
>> 10
>> 24
Mario Gleichmann JUG Frankfurt / Main59
Fundamental Functions : reduce
val or = ( x :Boolean ) => ( y :Boolean ) => x || y
val and = ( x :Boolean ) => ( y :Boolean ) => x && y
val not = ( x :Boolean ) => !x ... val existOneTrue = reduce ( or ) ( false ) _
val allFalse = not o reduce ( or ) ( false ) _
val allTrue = reduce ( and ) ( true ) _
... allTrue( List( 1 < 2, 3 == 3, 1 + 1 == 2 ) ) >> true
Mario Gleichmann JUG Frankfurt / Main60
Example: List concatenation
def append[T] (x:T) (xs:List[ _ ]) = x :: xs val listConcat = reduce ( append ) _
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )>> List( 8, 9, 1, 2, 3, 4 ) listConcat ( List( true, true, false ) ) ( List( true, false ) ) )>> List( true, false, true, true, false )
Mario Gleichmann JUG Frankfurt / Main61
Example: List concatenation
def append[T] (x:T) (xs:List[ _ ]) = x :: xs val listConcat = reduce ( append ) _
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )>> List( 8, 9, 1, 2, 3, 4 ) listConcat ( List( true, true, false ) ) ( List( true, false ) ) )>> List( true, false, true, true, false )
'unit''unit'
Mario Gleichmann JUG Frankfurt / Main62
Example: List concatenation
def append[T] (x:T) (xs:List[ _ ]) = x :: xs val listConcat = flipArgs( reduce ( append ) _ )
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )>> List( 1, 2, 3, 4, 8, 9 ) listConcat ( List( true, true, false ) ) ( List( true, false ) ) )>> List( true, true, false, true, false )
Mario Gleichmann JUG Frankfurt / Main63
Example: List concatenation
def append[T] (x:T) (xs:List[ _ ]) = x :: xs val listConcat = flipArgs( reduce ( append ) _ )
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )>> List( 1, 2, 3, 4, 8, 9 ) listConcat ( List( true, true, false ) ) ( List( true, false ) ) )>> List( true, true, false, true, false )
'unit''unit'
Mario Gleichmann JUG Frankfurt / Main64
Example: List concatenation
def append[T] (x:T) (xs:List[ _ ]) = x :: xs val listConcat = flipArgs( reduce ( append ) _ )
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )
Type of result: List[Any]
!
Mario Gleichmann JUG Frankfurt / Main65
Example: List concatenation
def append[T] (x:T) (xs:List[ T ]) = x :: xs def listConcat[A] ( a: List[A]) ( b: List[A] ) : List[A] = reduce[ A, List[A] ] ( ( append[A] ) _ ) (b) (a)
listConcat ( List( 1, 2, 3, 4 ) ) ( List( 8, 9 ) ) )
Type of result: List[Int]
Mario Gleichmann JUG Frankfurt / Main66
More
Fundamental Functions
Mario Gleichmann JUG Frankfurt / Main67
Fundamental Functions
val doubleAll : List[Int] => List[Int] = _ match {
case Nil => Nil
case y :: ys => y * 2 :: doubleAll( ys )}
doubleAll( List( 1, 2, 3, 4 ) )
>> List( 2, 4, 6, 8 )
Mario Gleichmann JUG Frankfurt / Main68
Fundamental Functions
val toLength : List[String] => List[Int] = _ match { case Nil => Nil case y :: ys => y.length :: toLength( ys )}
toLength( List( "a", "ab", "abc", "abcde" ) ) )
>> List( 1, 2, 3, 5 )
Mario Gleichmann JUG Frankfurt / Main69
Fundamental Functions : map
val map : List[T] => List[R] = _ match { case Nil => Nil case y :: ys => f?( y ) :: map( ys )}
Mario Gleichmann JUG Frankfurt / Main70
Fundamental Functions : map
def map[T, R] ( f: T => R ) : List[T] => List[R] = ( xs : List[T] ) => xs match { case Nil => Nil case y :: ys => f( y ) :: map( f )( ys ) }
Mario Gleichmann JUG Frankfurt / Main71
Fundamental Functions : map
def map[T, R] ( f: T => R ) : List[T] => List[R] = ( xs : List[T] ) => xs match { case Nil => Nil case y :: ys => f( y ) :: map( f )( ys ) }
Accepting a function to apply on every element
Resulting in another function,accepting the List of elements
Mario Gleichmann JUG Frankfurt / Main72
Fundamental Functions : map
def map[T, R] ( f: T => R ) : List[T] => List[R] = ...
...val mult = (x : Int) => (y : Int) => x * y
val doubleAll = map( mult( 2 ) )
...val toLength = map( ( s :String ) => s.length )
Mario Gleichmann JUG Frankfurt / Main73
Fundamental Functions : map
val add = (x : Int) => (y : Int) => x + y
val sum = reduce ( add ) ( 0 ) _ val sumMatrix = sum o map( sum )
Type of sum : List[Int] => Int
Type of map( sum ) : List[List[Int]] => List[Int]
Type of sumMatrix : List[List[Int]] => Int
Mario Gleichmann JUG Frankfurt / Main74
Fundamental Functions : map
val add = (x : Int) => (y : Int) => x + y
val sum = reduce ( add ) ( 0 ) _ val sumMatrix = sum o map( sum )
...val matrix = List( List( 1, 2, 3 ), List( 4, 5, 6 ), List( 7, 8, 9 ) )
sumMatrix( matrix ) >> 45
Mario Gleichmann JUG Frankfurt / Main75
map via reduce
def applyAndPrepend [A, B] ( f: A => B ) =
( a :A ) => ( bs :List[B] ) => f( a ) :: bs
def map [A, B] ( f: A => B ) : List[A] => List[B] =
reduce ( applyAndPrepend( f ) ) ( Nil ) _
Mario Gleichmann JUG Frankfurt / Main76
map via reduce
def applyAndPrepend [A, B] ( f: A => B ) =
( a :A ) => ( bs :List[B] ) => f( a ) :: bs
def map [A, B] ( f: A => B ) : List[A] => List[B] =
reduce ( applyAndPrepend( f ) ) ( Nil ) _
X1 :: X2 :: X3 :: X4 :: Nil
f :: f :: f :: f :: Nil
Mario Gleichmann JUG Frankfurt / Main77
filter via reduce
def filter[T]( pred :T => Boolean ) : List[T] => List[T] = reduce ( ( x :T ) => ( acc :List[T] ) =>
if( pred( x ) ) x :: acc else acc ) ( Nil )
Mario Gleichmann JUG Frankfurt / Main78
Type Classes
Mario Gleichmann JUG Frankfurt / Main79
Type Classes
def sum( xs : List[Int] ) : Int = xs match { case Nil => 0 case head :: tail => head + sum( tail )}
Mario Gleichmann JUG Frankfurt / Main80
Type Classes
def sum( xs : List[String] ) : String = xs match { case Nil => ““ case head :: tail => head + sum( tail )} Concatenation !
Mario Gleichmann JUG Frankfurt / Main81
Type Classes
def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? case head :: tail => head ? sum( tail )}
Mario Gleichmann JUG Frankfurt / Main82
Type Classes
def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? case head :: tail => head ? sum( tail )}
'neutral' element
element composition
Mario Gleichmann JUG Frankfurt / Main83
Type Classes
trait SemiGroup[S] { def add( x : S, y : S ) : S}
trait Monoid[M] extends SemiGroup[M] { def unit : M}
'neutral' element
element composition
Mario Gleichmann JUG Frankfurt / Main84
Type Classes
def sum [A] ( xs : List[A], m: Monoid[A] ) : A =
xs match { case Nil => m.unit case head :: tail => m.add( head, sum( tail, m) )
}
Mario Gleichmann JUG Frankfurt / Main85
Type Classes
def sum [A] ( xs : List[A], m: Monoid[A] ) : A =
xs match { case Nil => m.unit case head :: tail => m.add( head, sum( tail, m) )
}
'type constraint'
Mario Gleichmann JUG Frankfurt / Main86
Type Classes
object IntMonoid extends Monoid[Int]{
def add(x : Int, y : Int): Int = x + y
def unit : Int = 0}
…
sum( List( 1, 2, 3, 4 ), IntMonoid )
Mario Gleichmann JUG Frankfurt / Main87
Type Classes
def sum [A] ( xs : List[A] ) ( implicit m: Monoid[A] ) : A = ...
implicit object IntMonoid extends Monoid[Int]{ … }
…
import typeclasses.monoid._...
sum( List( 1, 2, 3, 4 ) )
Mario Gleichmann JUG Frankfurt / Main88
Example: Clock-Monoid
sealed abstract case class ClockHour( hour: Int )case object HOUR_0 extends ClockHour( 0 )case object HOUR_1 extends ClockHour( 1 )...case object HOUR_11 extends ClockHour( 11 )
…implicit object ClockHourMonoid extends Monoid[ClockHour]{ val hours = Array( HOUR_0, HOUR_1, HOUR_2, ..., HOUR_11 ) def add(x : ClockHour, y : ClockHour) = hours( ( x.hour + y.hour ) % 12 ) def unit : ClockHour = HOUR_0}
Mario Gleichmann JUG Frankfurt / Main89
Example: Clock-Monoid
import typeclasses.monoid.ClockHourMonoid...
sum( List( HOUR_5, HOUR_3, HOUR_8 ) )
>> HOUR_4
Mario Gleichmann JUG Frankfurt / Main90
Example: Contains element ?
trait Eq[E]{ def equals( e1 :E, e2 :E ) : Boolean}
def contains [T] ( x :T, xs : List[T] ) ( implicit eq :Eq[T] ) : Boolean = reduce
( ( y :T ) => ( acc :Boolean ) =>
if( eq.equals( x, y ) ) true else acc )( false )
( xs ) 2nd Arg: 'unit'
3rd Arg: List to reduce
1st Arg: 'reducing' function
Mario Gleichmann JUG Frankfurt / Main91
Example: Contains element ?
case class Person( name :String, age :Int ) implicit object PersonEq extends Eq[Person]{ def equals( p1 :Person, p2 :Person ) : Boolean =
(p1.name == p2.name) && (p1.age == p2.age)} ...val persons = List( Person("Hans", 22),
Person( "Helga", 32 ), Person( "Hugo", 47 ) )
contains( Person( "Olga", 22 ), persons ) >> falsecontains( Person( "Helga", 32 ), persons ) >> true
Mario Gleichmann JUG Frankfurt / Main92
Monads
Mario Gleichmann JUG Frankfurt / Main
Monads
A simple Monad: Option
Option[+A]
<< abstract >>
Some[+A] None
presence absence Handling the or of something
...as the result of a computation (computational environment)
Mario Gleichmann JUG Frankfurt / Main
Monads
A simple Monad: Option
class CustomerDAO{
def findCustomer( custId: Long ) : Option<Customer> = { ... if( found( customer ) ) Some( customer ) else None } }
Mario Gleichmann JUG Frankfurt / Main
Monads
A simple Monad: Option
class CustomerDAO{
def findCustomer( custId: Long ) : Option<Customer> = { ... if( found( customer ) ) Some( customer ) else None } }
Explicit Notion, that there may be 'none' result
Mario Gleichmann JUG Frankfurt / Main
Monads
val customerHit = customerDAO.findCustomer( 123 );...
customerHit match {
case Some( customer ) => println( customer.name )
case None => println( “not found“ )}
A simple Monad: Option
Mario Gleichmann JUG Frankfurt / Main
Monads
val customerHit = customerDAO.findCustomer( 123 );...
customerHit match {
case Some( customer ) => println( customer.name )
case None => println( “not found“ )}
A simple Monad: Option
Explicit Handling the absensce of a result
Forces 'Awareness'
Mario Gleichmann JUG Frankfurt / Main
Monads
val customerHit = customerDAO.findCustomer( 123 );...
customerHit match {
case Some( customer ) => println( customer.name )
case None => println( “not found“ )}
A simple Monad: Option
... beside from that ... what's the deal ???
Mario Gleichmann JUG Frankfurt / Main
Monads
val customerHit = customerDAO.findCustomer( 123 );...
customerHit match {
case Some( customer ) => println( customer.name )
case None => println( “not found“ )}
A simple Monad: Option
'Protected' Function Composition ...
Mario Gleichmann JUG Frankfurt / Main
Monads
val projects = Map( "Jan" -> "IKT", "Joe" -> "TensE", "Luca" -> "InTA" ) val customers = Map( "IKT" -> "Hanso GmbH", "InTA" -> "RAIA Duo" ) val cities = Map( "Hanso GmbH" -> "Stuttgart", "Mogno" -> "Mailand" )
Mario Gleichmann JUG Frankfurt / Main
Monads
Where is Jan ? Jan -> IKT -> Hanso GmbH -> Stuttgart
val projects = Map( "Jan" -> "IKT", "Joe" -> "TensE", "Luca" -> "InTA" ) val customers = Map( "IKT" -> "Hanso GmbH", "InTA" -> "RAIA Duo" ) val cities = Map( "Hanso GmbH" -> "Stuttgart", "Mogno" -> "Mailand" )
Mario Gleichmann JUG Frankfurt / Main
Monads
public String whereIs( String name ){String project = projects.get( name );if( project != null ){
String customer = customers.get( project );if( customer != null ){
String city = cities.get( customer )if( city != null ) return city; else return “unknown“;
}else return ''unknown'';
}else return ''unknown'';
}
Java
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer )
.getOrElse( "unknown!" )}
ScalaA simple Monad: Option
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer )
.getOrElse( "unknown!" )}
ScalaA simple Monad: Option
Results in Option[String]
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer )
.getOrElse( "unknown!" )}
ScalaA simple Monad: Option
Results in Option[String]
Option[A].map( A => B ) >> Option[B]
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer )
.getOrElse( "unknown!" )}
ScalaA simple Monad: Option
Results in Option[String]
Option[A].map( A => B ) >> Option[B] Option[A].map( A => Option[B] ) >> Option[Option[B]]
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer )
.getOrElse( "unknown!" )}
ScalaA simple Monad: Option
Results in Option[String]
Option[A].map( A => B ) >> Option[B] Option[A].map( A => Option[B] ) >> Option[Option[B]] Option[A].flatMap( A => Option[B] ) >> Option[B]
Mario Gleichmann JUG Frankfurt / Main
Monads
Some[A] Some[B]
None
map( A => B ) map( B => C )
None
Some[C]
None
Some[D]
map( C => D )
. . .
Protected Composition
Mario Gleichmann JUG Frankfurt / Main
Monads
def whereIs( name: String ) = ( for( project <- projects get name;
customer <- customers get project; city <- cities get customer
) yield city
).getOrElse( "unknown!" )
A simple Monad: Option
Mario Gleichmann JUG Frankfurt / Main
Summary
● Currying
● Combinators
● Fundamental Functions
● Type classes
● Monads
Scala allows for ...
Mario Gleichmann JUG Frankfurt / Main111
Thank you !
Mario Gleichmann JUG Frankfurt / Main112
References
Scala Home www.scala-lang.org
Dr. Erik Meijer C9 Lectures – Functional Programming Fundamentalshttp://channel9.msdn.com
Graham Hutton Programming in HaskellCambridge
Odersky, Spoon, Venners Programming in Scalaartima