Functional Scaλa … in Practice 20.10.2010 Java User Group Frankfurt / Main Mario Gleichmann Mario Gleichmann JUG Frankfurt / Main
Introduction Mario Gleichmann site: www.mg-informatik.de blog: 'brain driven development' gleichmann.wordpress.com mail: mario.gleichmann@mg-informatik.de Mario Gleichmann 2 JUG Frankfurt / Main
Summary Mario Gleichmann 3 JUG Frankfurt / Main
What is Functional Programming ? Functional Style val sum = fold( 1 to 10, add ) Expression based Computation method is function application Mario Gleichmann 4 fcn JUG Frankfurt / Main
What makes a Function ? Function Types val add = ( x :Int, y :Int ) => x + y Type of add : ( Int , Int ) => Int Mario Gleichmann 5 Obj JUG Frankfurt / Main
Closures 'closing over' var limit = 18 val isAdult = ( age :Int ) => age >= limit 'Closed term' Mario Gleichmann 6 appl JUG Frankfurt / Main
Algebraic Datatypes Example - Tree 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 Mario Gleichmann 7 insrt JUG Frankfurt / Main
Pattern Matching val depth :Tree => Int = _ match { case Empty => 0 case Leaf( _ ) => 1 case Node( _, left, right ) => 1 + max( depth( left ), depth( right ) ) } Mario Gleichmann 8 Prt isb JUG Frankfurt / Main
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 Mario Gleichmann 9 chn JUG Frankfurt / Main
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 Mario Gleichmann 10 Hgh rdr Fcn JUG Frankfurt / Main
Higher Order Functions val filter = ( predicate: Int => Boolean , xs :List[Int] ) => { for( x <- xs; if predicate( x ) ) yield x } Mario Gleichmann 11 Fcn Tp JUG Frankfurt / Main
Lambda Expressions filter( num => num % 2 == 0, List( 1, 2, 3, 4, 5, 6 ) ) … filter( _ > 0, List( -1, 5, 0, -3, 7 ) ) Mario Gleichmann 12 fst arg JUG Frankfurt / Main
Partial Application Mario Gleichmann 13 JUG Frankfurt / Main
Partial Application val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) Type of isPrime: Int => Boolean Mario Gleichmann 14 def prms wthn JUG Frankfurt / Main
Partial Application Delegation val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs ) Type of filter: ( Int => Boolean, List[Int] ) => List[Int] Mario Gleichmann 15 fxd var JUG Frankfurt / Main
Partial Application Delegation val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs ) fixed variable Mario Gleichmann 16 appl nappl JUG Frankfurt / Main
Partial Application New from Old ... val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = filter( isPrime, _ :List[Int] ) applied unapplied Mario Gleichmann 17 Fcn Tp JUG Frankfurt / Main
Partial Application New from Old ... val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = filter( isPrime, _ :List[Int] ) Type of primesWithin: List[Int] => List[Int] Mario Gleichmann 18 Crrng JUG Frankfurt / Main
Currying Mario Gleichmann 19 JUG Frankfurt / Main
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] Mario Gleichmann 20 Crrd fcn JUG Frankfurt / Main
Currying 'Higher Order Lambda Closures' ... val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => { for( x <- xs; if pred( x ) ) yield x } Mario Gleichmann 21 Crrd fcn Tp JUG Frankfurt / Main
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] Mario Gleichmann 22 sngl arg JUG Frankfurt / Main
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] Curried Function Curried Function Mario Gleichmann 23 sngl xpln arg JUG Frankfurt / Main
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] ... accepting one ... resulting in another A function (Function) Arg function Mario Gleichmann 24 lmbd xpr JUG Frankfurt / Main
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 Mario Gleichmann 25 cls ovr JUG Frankfurt / Main
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' Mario Gleichmann 26 Smp prms wthn JUG Frankfurt / Main
Example 1 – Extract primes val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => { for( x <- xs; if pred( x ) ) yield x } val primesWithin = filter( isPrime ) Mario Gleichmann 27 Fcn Tp JUG Frankfurt / Main
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] Mario Gleichmann 28 appl JUG Frankfurt / Main
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 ) Mario Gleichmann 29 Smp rc mgmt JUG Frankfurt / Main
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 } } } Mario Gleichmann 30 appl JUG Frankfurt / Main
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 ) { .... } } Mario Gleichmann 31 smmy JUG Frankfurt / Main
Combinators Mario Gleichmann 32 JUG Frankfurt / Main
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 33 JUG Frankfurt / Main
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 34 JUG Frankfurt / Main
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 35 JUG Frankfurt / Main
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 Mario Gleichmann 36 JUG Frankfurt / Main
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 37 JUG Frankfurt / Main
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 38 JUG Frankfurt / Main
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 39 JUG Frankfurt / Main
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 40 JUG Frankfurt / Main
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 ) Postfix ... >> 16 Mario Gleichmann 41 JUG Frankfurt / Main
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 ) Infix !!! >> 16 Mario Gleichmann 42 JUG Frankfurt / Main
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 ) 'Point free style' of function definition >> 16 Mario Gleichmann 43 JUG Frankfurt / Main
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 44 JUG Frankfurt / Main
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 45 JUG Frankfurt / Main
Example: Word Counter Basic Functions 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 Mario Gleichmann 46 JUG Frankfurt / Main
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 47 JUG Frankfurt / Main
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 vs. val evenWordCount = (sentence :String ) => size ( filter( even ) ( length( words( sentence ) ) ) ) Mario Gleichmann 48 JUG Frankfurt / Main
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 49 JUG Frankfurt / Main
Fundamental Functions Mario Gleichmann 50 JUG Frankfurt / Main
Fundamental Functions def sum ( list : List[Int] ) : Int = list match { case Nil => 0 case x :: xs => x + sum( xs ) } Mario Gleichmann 51 JUG Frankfurt / Main
Fundamental Functions def product ( list : List[Int] ) : Int = list match { case Nil => 1 case x :: xs => x * product( xs ) } Mario Gleichmann 52 JUG Frankfurt / Main
Fundamental Functions def allTrue ( ys : List[Boolean] ) : Boolean = ys match { case Nil => true case x :: xs => x and allTrue( xs ) } Mario Gleichmann 53 JUG Frankfurt / Main
Fundamental Functions : reduce def reduce ( list : List[ T ] ) : R = list match { case Nil => ? case x :: xs => x ʘ reduce( xs ) } Mario Gleichmann 54 JUG Frankfurt / Main
Fundamental Functions : reduce def reduce ( list : List[ T ] ) : R = list match { case Nil => ? 'unit' case x :: xs => x ʘ reduce( xs ) } 'subsuming operation' Mario Gleichmann 55 JUG Frankfurt / Main
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 56 JUG Frankfurt / Main
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 57 JUG Frankfurt / Main
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 ) ) >> 10 product( List( 1, 2, 3, 4 ) ) >> 24 Mario Gleichmann 58 JUG Frankfurt / Main
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 59 JUG Frankfurt / Main
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 60 JUG Frankfurt / Main
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 ) 'unit' listConcat ( List( true, true, false ) ) ( List( true, false ) ) ) >> List( true, false, true, true, false ) Mario Gleichmann 61 JUG Frankfurt / Main
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 62 JUG Frankfurt / Main
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 ) 'unit' listConcat ( List( true, true, false ) ) ( List( true, false ) ) ) >> List( true, true, false, true, false ) Mario Gleichmann 63 JUG Frankfurt / Main
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 64 JUG Frankfurt / Main
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 65 JUG Frankfurt / Main
More Fundamental Functions Mario Gleichmann 66 JUG Frankfurt / Main
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 67 JUG Frankfurt / Main
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 68 JUG Frankfurt / Main
Fundamental Functions : map val map : List[T] => List[R] = _ match { case Nil => Nil case y :: ys => f?( y ) :: map( ys ) } Mario Gleichmann 69 JUG Frankfurt / Main
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 70 JUG Frankfurt / Main
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 Resulting in another function, apply on every element accepting the List of elements Mario Gleichmann 71 JUG Frankfurt / Main
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 72 JUG Frankfurt / Main
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 73 JUG Frankfurt / Main
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 74 JUG Frankfurt / Main
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 75 JUG Frankfurt / Main
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 ) _ f :: f :: f :: f :: Nil X1 :: X2 :: X3 :: X4 :: Nil Mario Gleichmann 76 JUG Frankfurt / Main
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 77 JUG Frankfurt / Main
Type Classes Mario Gleichmann 78 JUG Frankfurt / Main
Type Classes def sum( xs : List[Int] ) : Int = xs match { case Nil => 0 case head :: tail => head + sum( tail ) } Mario Gleichmann 79 JUG Frankfurt / Main
Type Classes def sum( xs : List[String] ) : String = xs match { case Nil => ““ case head :: tail => head + sum( tail ) } Concatenation ! Mario Gleichmann 80 JUG Frankfurt / Main
Type Classes def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? case head :: tail => head ? sum( tail ) } Mario Gleichmann 81 JUG Frankfurt / Main
Type Classes def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? 'neutral' element case head :: tail => head ? sum( tail ) } element composition Mario Gleichmann 82 JUG Frankfurt / Main
Type Classes trait SemiGroup[S] { def add( x : S, y : S ) : S element composition } trait Monoid[M] extends SemiGroup[M] { def unit : M 'neutral' element } Mario Gleichmann 83 JUG Frankfurt / Main
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 84 JUG Frankfurt / Main
Type Classes def sum [A] ( xs : List[A], m: Monoid[A] ) : A = 'type constraint' xs match { case Nil => m.unit case head :: tail => m.add( head, sum( tail, m) ) } Mario Gleichmann 85 JUG Frankfurt / Main
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 86 JUG Frankfurt / Main
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 87 JUG Frankfurt / Main
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 88 JUG Frankfurt / Main
Example: Clock-Monoid import typeclasses.monoid.ClockHourMonoid ... sum( List( HOUR_5, HOUR_3, HOUR_8 ) ) >> HOUR_4 Mario Gleichmann 89 JUG Frankfurt / Main
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 ) 1st Arg: 'reducing' function ( xs ) 2nd Arg: 'unit' 3rd Arg: List to reduce Mario Gleichmann 90 JUG Frankfurt / Main
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 ) >> false contains( Person( "Helga", 32 ), persons ) >> true Mario Gleichmann 91 JUG Frankfurt / Main
Monads Mario Gleichmann 92 JUG Frankfurt / Main
Monads A simple Monad: Option << abstract >> Option[+A] 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 A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } Mario Gleichmann JUG Frankfurt / Main
Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } Explicit Handling the absensce of a result Forces 'Awareness' Mario Gleichmann JUG Frankfurt / Main
Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } ... beside from that ... what's the deal ??? Mario Gleichmann JUG Frankfurt / Main
Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } '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 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" ) Where is Jan ? Jan -> IKT -> Hanso GmbH -> Stuttgart Mario Gleichmann JUG Frankfurt / Main
Monads Jav public String whereIs( String name ){ a 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''; } Mario Gleichmann JUG Frankfurt / Main
Monads Scal A simple Monad: Option a def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Mario Gleichmann JUG Frankfurt / Main
Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Mario Gleichmann JUG Frankfurt / Main
Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Option[A].map( A => B ) >> Option[B] Mario Gleichmann JUG Frankfurt / Main
Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Option[A].map( A => B ) >> Option[B] Option[A].map( A => Option[B] ) >> Option[Option[B]] Mario Gleichmann JUG Frankfurt / Main
Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } 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 Protected Composition map( A => B ) map( B => C ) map( C => D ) None None None ... Some[A] Some[B] Some[C] Some[D] Mario Gleichmann JUG Frankfurt / Main
Monads A simple Monad: Option def whereIs( name: String ) = ( for( project <- projects get name; customer <- customers get project; city <- cities get customer ) yield city ).getOrElse( "unknown!" ) Mario Gleichmann JUG Frankfurt / Main
Summary Scala allows for ... ● Currying ● Combinators ● Fundamental Functions ● Type classes ● Monads Mario Gleichmann JUG Frankfurt / Main
Thank you ! Mario Gleichmann 111 JUG Frankfurt / Main
References Scala Home www.scala-lang.org Dr. Erik Meijer C9 Lectures – Functional Programming Fundamentals http://channel9.msdn.com Graham Hutton Programming in Haskell Cambridge Odersky, Spoon, Venners Programming in Scala artima Mario Gleichmann 112 JUG Frankfurt / Main

Functional Scala II (in practice)

  • 1.
    Functional Scaλa … in Practice 20.10.2010 Java User Group Frankfurt / Main Mario Gleichmann Mario Gleichmann JUG Frankfurt / Main
  • 2.
    Introduction Mario Gleichmann site: www.mg-informatik.de blog: 'brain driven development' gleichmann.wordpress.com mail: mario.gleichmann@mg-informatik.de Mario Gleichmann 2 JUG Frankfurt / Main
  • 3.
    Summary Mario Gleichmann 3 JUG Frankfurt / Main
  • 4.
    What is FunctionalProgramming ? Functional Style val sum = fold( 1 to 10, add ) Expression based Computation method is function application Mario Gleichmann 4 fcn JUG Frankfurt / Main
  • 5.
    What makes aFunction ? Function Types val add = ( x :Int, y :Int ) => x + y Type of add : ( Int , Int ) => Int Mario Gleichmann 5 Obj JUG Frankfurt / Main
  • 6.
    Closures 'closing over' var limit = 18 val isAdult = ( age :Int ) => age >= limit 'Closed term' Mario Gleichmann 6 appl JUG Frankfurt / Main
  • 7.
    Algebraic Datatypes Example - Tree 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 Mario Gleichmann 7 insrt JUG Frankfurt / Main
  • 8.
    Pattern Matching val depth :Tree => Int = _ match { case Empty => 0 case Leaf( _ ) => 1 case Node( _, left, right ) => 1 + max( depth( left ), depth( right ) ) } Mario Gleichmann 8 Prt isb JUG Frankfurt / Main
  • 9.
    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 Mario Gleichmann 9 chn JUG Frankfurt / Main
  • 10.
    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 Mario Gleichmann 10 Hgh rdr Fcn JUG Frankfurt / Main
  • 11.
    Higher Order Functions val filter = ( predicate: Int => Boolean , xs :List[Int] ) => { for( x <- xs; if predicate( x ) ) yield x } Mario Gleichmann 11 Fcn Tp JUG Frankfurt / Main
  • 12.
    Lambda Expressions filter( num => num % 2 == 0, List( 1, 2, 3, 4, 5, 6 ) ) … filter( _ > 0, List( -1, 5, 0, -3, 7 ) ) Mario Gleichmann 12 fst arg JUG Frankfurt / Main
  • 13.
    Partial Application Mario Gleichmann 13 JUG Frankfurt / Main
  • 14.
    Partial Application val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) Type of isPrime: Int => Boolean Mario Gleichmann 14 def prms wthn JUG Frankfurt / Main
  • 15.
    Partial Application Delegation val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs ) Type of filter: ( Int => Boolean, List[Int] ) => List[Int] Mario Gleichmann 15 fxd var JUG Frankfurt / Main
  • 16.
    Partial Application Delegation val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = ( xs :List[Int] ) => filter( isPrime , xs ) fixed variable Mario Gleichmann 16 appl nappl JUG Frankfurt / Main
  • 17.
    Partial Application New from Old ... val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = filter( isPrime, _ :List[Int] ) applied unapplied Mario Gleichmann 17 Fcn Tp JUG Frankfurt / Main
  • 18.
    Partial Application New from Old ... val isPrime = (x :Int) => ( 2 to x/2 ).forall( x % _ != 0 ) val primesWithin = filter( isPrime, _ :List[Int] ) Type of primesWithin: List[Int] => List[Int] Mario Gleichmann 18 Crrng JUG Frankfurt / Main
  • 19.
    Currying Mario Gleichmann 19 JUG Frankfurt / Main
  • 20.
    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] Mario Gleichmann 20 Crrd fcn JUG Frankfurt / Main
  • 21.
    Currying 'Higher Order Lambda Closures' ... val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => { for( x <- xs; if pred( x ) ) yield x } Mario Gleichmann 21 Crrd fcn Tp JUG Frankfurt / Main
  • 22.
    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] Mario Gleichmann 22 sngl arg JUG Frankfurt / Main
  • 23.
    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] Curried Function Curried Function Mario Gleichmann 23 sngl xpln arg JUG Frankfurt / Main
  • 24.
    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] ... accepting one ... resulting in another A function (Function) Arg function Mario Gleichmann 24 lmbd xpr JUG Frankfurt / Main
  • 25.
    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 Mario Gleichmann 25 cls ovr JUG Frankfurt / Main
  • 26.
    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' Mario Gleichmann 26 Smp prms wthn JUG Frankfurt / Main
  • 27.
    Example 1 –Extract primes val filter = ( pred: Int => Boolean ) => ( xs :List[Int] ) => { for( x <- xs; if pred( x ) ) yield x } val primesWithin = filter( isPrime ) Mario Gleichmann 27 Fcn Tp JUG Frankfurt / Main
  • 28.
    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] Mario Gleichmann 28 appl JUG Frankfurt / Main
  • 29.
    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 ) Mario Gleichmann 29 Smp rc mgmt JUG Frankfurt / Main
  • 30.
    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 } } } Mario Gleichmann 30 appl JUG Frankfurt / Main
  • 31.
    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 ) { .... } } Mario Gleichmann 31 smmy JUG Frankfurt / Main
  • 32.
    Combinators Mario Gleichmann 32 JUG Frankfurt / Main
  • 33.
    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 33 JUG Frankfurt / Main
  • 34.
    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 34 JUG Frankfurt / Main
  • 35.
    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 35 JUG Frankfurt / Main
  • 36.
    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 Mario Gleichmann 36 JUG Frankfurt / Main
  • 37.
    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 37 JUG Frankfurt / Main
  • 38.
    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 38 JUG Frankfurt / Main
  • 39.
    Combinators : FunctionComposition 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 39 JUG Frankfurt / Main
  • 40.
    Combinators : FunctionComposition 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 40 JUG Frankfurt / Main
  • 41.
    Combinators : FunctionComposition 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 ) Postfix ... >> 16 Mario Gleichmann 41 JUG Frankfurt / Main
  • 42.
    Combinators : FunctionComposition 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 ) Infix !!! >> 16 Mario Gleichmann 42 JUG Frankfurt / Main
  • 43.
    Combinators : FunctionComposition 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 ) 'Point free style' of function definition >> 16 Mario Gleichmann 43 JUG Frankfurt / Main
  • 44.
    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 44 JUG Frankfurt / Main
  • 45.
    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 45 JUG Frankfurt / Main
  • 46.
    Example: Word Counter Basic Functions 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 Mario Gleichmann 46 JUG Frankfurt / Main
  • 47.
    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 47 JUG Frankfurt / Main
  • 48.
    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 vs. val evenWordCount = (sentence :String ) => size ( filter( even ) ( length( words( sentence ) ) ) ) Mario Gleichmann 48 JUG Frankfurt / Main
  • 49.
    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 49 JUG Frankfurt / Main
  • 50.
  • 51.
    Fundamental Functions def sum ( list : List[Int] ) : Int = list match { case Nil => 0 case x :: xs => x + sum( xs ) } Mario Gleichmann 51 JUG Frankfurt / Main
  • 52.
    Fundamental Functions def product ( list : List[Int] ) : Int = list match { case Nil => 1 case x :: xs => x * product( xs ) } Mario Gleichmann 52 JUG Frankfurt / Main
  • 53.
    Fundamental Functions def allTrue ( ys : List[Boolean] ) : Boolean = ys match { case Nil => true case x :: xs => x and allTrue( xs ) } Mario Gleichmann 53 JUG Frankfurt / Main
  • 54.
    Fundamental Functions :reduce def reduce ( list : List[ T ] ) : R = list match { case Nil => ? case x :: xs => x ʘ reduce( xs ) } Mario Gleichmann 54 JUG Frankfurt / Main
  • 55.
    Fundamental Functions :reduce def reduce ( list : List[ T ] ) : R = list match { case Nil => ? 'unit' case x :: xs => x ʘ reduce( xs ) } 'subsuming operation' Mario Gleichmann 55 JUG Frankfurt / Main
  • 56.
    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 56 JUG Frankfurt / Main
  • 57.
    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 57 JUG Frankfurt / Main
  • 58.
    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 ) ) >> 10 product( List( 1, 2, 3, 4 ) ) >> 24 Mario Gleichmann 58 JUG Frankfurt / Main
  • 59.
    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 59 JUG Frankfurt / Main
  • 60.
    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 60 JUG Frankfurt / Main
  • 61.
    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 ) 'unit' listConcat ( List( true, true, false ) ) ( List( true, false ) ) ) >> List( true, false, true, true, false ) Mario Gleichmann 61 JUG Frankfurt / Main
  • 62.
    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 62 JUG Frankfurt / Main
  • 63.
    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 ) 'unit' listConcat ( List( true, true, false ) ) ( List( true, false ) ) ) >> List( true, true, false, true, false ) Mario Gleichmann 63 JUG Frankfurt / Main
  • 64.
    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 64 JUG Frankfurt / Main
  • 65.
    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 65 JUG Frankfurt / Main
  • 66.
    More Fundamental Functions Mario Gleichmann 66 JUG Frankfurt / Main
  • 67.
    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 67 JUG Frankfurt / Main
  • 68.
    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 68 JUG Frankfurt / Main
  • 69.
    Fundamental Functions :map val map : List[T] => List[R] = _ match { case Nil => Nil case y :: ys => f?( y ) :: map( ys ) } Mario Gleichmann 69 JUG Frankfurt / Main
  • 70.
    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 70 JUG Frankfurt / Main
  • 71.
    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 Resulting in another function, apply on every element accepting the List of elements Mario Gleichmann 71 JUG Frankfurt / Main
  • 72.
    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 72 JUG Frankfurt / Main
  • 73.
    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 73 JUG Frankfurt / Main
  • 74.
    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 74 JUG Frankfurt / Main
  • 75.
    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 75 JUG Frankfurt / Main
  • 76.
    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 ) _ f :: f :: f :: f :: Nil X1 :: X2 :: X3 :: X4 :: Nil Mario Gleichmann 76 JUG Frankfurt / Main
  • 77.
    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 77 JUG Frankfurt / Main
  • 78.
    Type Classes Mario Gleichmann 78 JUG Frankfurt / Main
  • 79.
    Type Classes def sum( xs : List[Int] ) : Int = xs match { case Nil => 0 case head :: tail => head + sum( tail ) } Mario Gleichmann 79 JUG Frankfurt / Main
  • 80.
    Type Classes def sum( xs : List[String] ) : String = xs match { case Nil => ““ case head :: tail => head + sum( tail ) } Concatenation ! Mario Gleichmann 80 JUG Frankfurt / Main
  • 81.
    Type Classes def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? case head :: tail => head ? sum( tail ) } Mario Gleichmann 81 JUG Frankfurt / Main
  • 82.
    Type Classes def sum[A]( xs : List[A] ) : A = xs match { case Nil => ? 'neutral' element case head :: tail => head ? sum( tail ) } element composition Mario Gleichmann 82 JUG Frankfurt / Main
  • 83.
    Type Classes trait SemiGroup[S] { def add( x : S, y : S ) : S element composition } trait Monoid[M] extends SemiGroup[M] { def unit : M 'neutral' element } Mario Gleichmann 83 JUG Frankfurt / Main
  • 84.
    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 84 JUG Frankfurt / Main
  • 85.
    Type Classes def sum [A] ( xs : List[A], m: Monoid[A] ) : A = 'type constraint' xs match { case Nil => m.unit case head :: tail => m.add( head, sum( tail, m) ) } Mario Gleichmann 85 JUG Frankfurt / Main
  • 86.
    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 86 JUG Frankfurt / Main
  • 87.
    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 87 JUG Frankfurt / Main
  • 88.
    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 88 JUG Frankfurt / Main
  • 89.
    Example: Clock-Monoid import typeclasses.monoid.ClockHourMonoid ... sum( List( HOUR_5, HOUR_3, HOUR_8 ) ) >> HOUR_4 Mario Gleichmann 89 JUG Frankfurt / Main
  • 90.
    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 ) 1st Arg: 'reducing' function ( xs ) 2nd Arg: 'unit' 3rd Arg: List to reduce Mario Gleichmann 90 JUG Frankfurt / Main
  • 91.
    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 ) >> false contains( Person( "Helga", 32 ), persons ) >> true Mario Gleichmann 91 JUG Frankfurt / Main
  • 92.
    Monads Mario Gleichmann 92 JUG Frankfurt / Main
  • 93.
    Monads A simple Monad: Option << abstract >> Option[+A] Some[+A] None presence absence Handling the or of something ...as the result of a computation (computational environment) Mario Gleichmann JUG Frankfurt / Main
  • 94.
    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
  • 95.
    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
  • 96.
    Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } Mario Gleichmann JUG Frankfurt / Main
  • 97.
    Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } Explicit Handling the absensce of a result Forces 'Awareness' Mario Gleichmann JUG Frankfurt / Main
  • 98.
    Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } ... beside from that ... what's the deal ??? Mario Gleichmann JUG Frankfurt / Main
  • 99.
    Monads A simple Monad: Option val customerHit = customerDAO.findCustomer( 123 ); ... customerHit match { case Some( customer ) => println( customer.name ) case None => println( “not found“ ) } 'Protected' Function Composition ... Mario Gleichmann JUG Frankfurt / Main
  • 100.
    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
  • 101.
    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" ) Where is Jan ? Jan -> IKT -> Hanso GmbH -> Stuttgart Mario Gleichmann JUG Frankfurt / Main
  • 102.
    Monads Jav public String whereIs( String name ){ a 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''; } Mario Gleichmann JUG Frankfurt / Main
  • 103.
    Monads Scal A simple Monad: Option a def whereIs( name: String ) = { projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Mario Gleichmann JUG Frankfurt / Main
  • 104.
    Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Mario Gleichmann JUG Frankfurt / Main
  • 105.
    Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Option[A].map( A => B ) >> Option[B] Mario Gleichmann JUG Frankfurt / Main
  • 106.
    Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } Option[A].map( A => B ) >> Option[B] Option[A].map( A => Option[B] ) >> Option[Option[B]] Mario Gleichmann JUG Frankfurt / Main
  • 107.
    Monads Scal A simple Monad: Option a def whereIs( name: String ) = { Results in Option[String] projects.get( name ) .flatMap( project => customers get project ) .flatMap( customer => cities get customer ) .getOrElse( "unknown!" ) } 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
  • 108.
    Monads Protected Composition map( A => B ) map( B => C ) map( C => D ) None None None ... Some[A] Some[B] Some[C] Some[D] Mario Gleichmann JUG Frankfurt / Main
  • 109.
    Monads A simple Monad: Option def whereIs( name: String ) = ( for( project <- projects get name; customer <- customers get project; city <- cities get customer ) yield city ).getOrElse( "unknown!" ) Mario Gleichmann JUG Frankfurt / Main
  • 110.
    Summary Scala allows for ... ● Currying ● Combinators ● Fundamental Functions ● Type classes ● Monads Mario Gleichmann JUG Frankfurt / Main
  • 111.
    Thank you ! MarioGleichmann 111 JUG Frankfurt / Main
  • 112.
    References Scala Home www.scala-lang.org Dr. Erik Meijer C9 Lectures – Functional Programming Fundamentals http://channel9.msdn.com Graham Hutton Programming in Haskell Cambridge Odersky, Spoon, Venners Programming in Scala artima Mario Gleichmann 112 JUG Frankfurt / Main