Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Groovy

Functional Groovy

Groovy can also be viewed as a functional language, thanks to its closures, and many other features

Guillaume Laforge

May 15, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Programming

Transcript

  1. © 2014 Guillaume Laforge. All rights reserved. Do not distribute

    without permission. Guillaume Laforge 
 @glaforge Functional Groovy
  2. Pivotal related talks • Thursday – 2:15pm - 3:15pm •Advanced

    Web Development Techniques with Grails 2 — Jeff Brown •Making the Eclipse IDE fun again — Martin Lippert ! • Friday – 9:00am - 5:00pm •From the database into the web: End-to-end REST web services with Spring — Oliver Gierke 4
  3. class  MathSpec  extends  Specification  {          def

     "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   }
  4. new  MarkupBuilder().html  {          head  {  

                   title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   }
  5. @RestController   class  App  {          @RequestMapping("/")

             String  home()  {  "Hello  World!"  }   } Speaking of conciseness... A full Spring app in the span of a tweet!
  6. GVM

  7. What is a functional language? Functions, closures, lambdas Immutability Side-effect

    free / pure Persistent collections Recursion Higher Order Functions Partial application Concurrency, parallelism Monads, functors, combinators... Composition Map / filter / reduce Lazy vs Eager Memoization Infinite streams
  8. Closures everywhere in Groovy APIs • Closures are used in

    many situations: – iterators – callbacks – higher-order functions – custom control structures – dynamic method definition – resource allocation and handling – threads creation and launch – continuation-like coding – ... 26
  9. Closures — the basics 27 def  adder  =  {  a,

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab'
  10. Closures — the basics 27 def  adder  =  {  a,

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Closure parameters
  11. Closures — the basics 27 def  adder  =  {  a,

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Assign a function into a variable Closure parameters
  12. Closures — the basics 27 def  adder  =  {  a,

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Short form of: adder.call(‘a’, ‘b’) Assign a function into a variable Closure parameters
  13. Closures — the basics 27 def  adder  =  {  a,

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Short form of: adder.call(‘a’, ‘b’) Genericity with duck typing & operator overloading Assign a function into a variable Closure parameters
  14. Closures — explicit types 28 ! def  intAdder  =  {

     int  a,  int  b  -­‐>  a  +  b  }  
  15. Closures — explicit types 28 ! def  intAdder  =  {

     int  a,  int  b  -­‐>  a  +  b  }   Be explicit about the types
  16. Closures — implicit parameter 29 def  doubler  =  {  it

     *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa'
  17. Closures — implicit parameter 29 def  doubler  =  {  it

     *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa' Implicit parameter
  18. Closures — implicit parameter 29 def  doubler  =  {  it

     *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa' Implicit parameter Multiply also defined on strings
  19. Closures — variable arguments 30 def  sum  =  {  ...

     elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc'
  20. Closures — variable arguments 30 def  sum  =  {  ...

     elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' Variable number of arguments
  21. Closures — variable arguments 30 def  sum  =  {  ...

     elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' You can specify the type: int... Variable number of arguments
  22. Closures — default values 31 def  mult  =  {  int

     a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50
  23. Closures — default values 31 def  mult  =  {  int

     a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 Default value
  24. Closures — default values 31 def  mult  =  {  int

     a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 Default value Provided value for b
  25. Closures — default values 31 def  mult  =  {  int

     a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 Default value Provided value for b Default value used for b
  26. Closures — methods as functions 32 def  logBase10  =  Math.&log10

      def  printer  =  System.out.&println   ! assert  logBase10(10)  ==  1   printer  'abc'
  27. Closures — methods as functions 32 def  logBase10  =  Math.&log10

      def  printer  =  System.out.&println   ! assert  logBase10(10)  ==  1   printer  'abc' Turn a method into a closure function
  28. Closures — iterations 33 def  fruits  =  ['apple',  
  

                             'orange',  
                            'banana']   ! fruits.each  {  println  it  }   fruits.each  printer
  29. Closures — iterations 33 def  fruits  =  ['apple',  
  

                             'orange',  
                            'banana']   ! fruits.each  {  println  it  }   fruits.each  printer Internal iteration
  30. Closures — iterations 33 def  fruits  =  ['apple',  
  

                             'orange',  
                            'banana']   ! fruits.each  {  println  it  }   fruits.each  printer Internal iteration Pass a method closure
  31. Closures — infinite streams 34 def  ints  =  1..1000  

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12]
  32. Closures — infinite streams 34 def  ints  =  1..1000  

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12] Could use an infinite iterator
  33. Closures — infinite streams 34 def  ints  =  1..1000  

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12] Could use an infinite iterator take(), takeWhile(), drop(), dropWhile()
  34. Closures — infinite streams 34 def  ints  =  1..1000  

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12] Could use an infinite iterator take(), takeWhile(), drop(), dropWhile() Returns an iterator
  35. Closures — infinite streams 34 def  ints  =  1..1000  

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12] Could use an infinite iterator take(), takeWhile(), drop(), dropWhile() Returns an iterator Variants for maps too
  36. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 }
  37. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 }
  38. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ]
  39. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ]
  40. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ')
  41. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ')
  42. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') assert  names  ==  "ERINE,  MARION"
  43. Closures — map / filter / reduce 36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  6),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') assert  names  ==  "ERINE,  MARION" find/findAll, inject, collect, flatten, min/max, unique, reverse, collate, groupBy, any/ every, head/tail/last, count/ countBy, combinations/ permutations/subsequences/ transpose, withDefault/ withLazyDefault
  44. Closures — Closures vs Java 8 lambdas? 37 IntStream.range(1,  100).forEach(s

     -­‐>  System.out.println(s));   ! Files.lines(Paths.get('README.adoc'))            .map(it  -­‐>  it.toUpperCase())            .forEach(it  -­‐>  System.out.println  it);
  45. Closures — Closures vs Java 8 lambdas? 37 IntStream.range(1,  100).forEach

     {  println  it  }   ! Files.lines(Paths.get('README.adoc'))            .map  {  it.toUpperCase()  }            .forEach  {  println  it  } IntStream.range(1,  100).forEach(s  -­‐>  System.out.println(s));   ! Files.lines(Paths.get('README.adoc'))            .map(it  -­‐>  it.toUpperCase())            .forEach(it  -­‐>  System.out.println  it);
  46. Closures — Closures vs Java 8 lambdas? 37 IntStream.range(1,  100).forEach

     {  println  it  }   ! Files.lines(Paths.get('README.adoc'))            .map  {  it.toUpperCase()  }            .forEach  {  println  it  } IntStream.range(1,  100).forEach(s  -­‐>  System.out.println(s));   ! Files.lines(Paths.get('README.adoc'))            .map(it  -­‐>  it.toUpperCase())            .forEach(it  -­‐>  System.out.println  it); Use Groovy closures wherever you pass lambdas in Java 8
  47. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {
  48. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }
  49. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }
  50. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }        assert  oneStarters.collection  ==
                [1,  16,  100,  121,  144,  169,  196]
  51. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }        assert  oneStarters.collection  ==
                [1,  16,  100,  121,  144,  169,  196]
  52. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }        assert  oneStarters.collection  ==
                [1,  16,  100,  121,  144,  169,  196]        assert  oneStarters.max()  ==  196
        assert  oneStarters.reduce  {  a,  b  -­‐>  a+b  }  ==  747
        assert  oneStarters.sum()  ==  747
 }
  53. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }        assert  oneStarters.collection  ==
                [1,  16,  100,  121,  144,  169,  196]        assert  oneStarters.max()  ==  196
        assert  oneStarters.reduce  {  a,  b  -­‐>  a+b  }  ==  747
        assert  oneStarters.sum()  ==  747
 } in parallel!
  54. Closures — map / filter / reduce & GPars 38

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }        assert  oneStarters.collection  ==
                [1,  16,  100,  121,  144,  169,  196]        assert  oneStarters.max()  ==  196
        assert  oneStarters.reduce  {  a,  b  -­‐>  a+b  }  ==  747
        assert  oneStarters.sum()  ==  747
 } in parallel! result still «parallel»
  55. GPars • Groovy library bundled with the distribution ! •

    Provides: – actors – safe agents – dataflow concurrency – data parallelism •fork / join, map / filter / reduce – asynchronous functions – parallel arrays – java.util.concurrent sugar 39
  56. GPars • Groovy library bundled with the distribution ! •

    Provides: – actors – safe agents – dataflow concurrency – data parallelism •fork / join, map / filter / reduce – asynchronous functions – parallel arrays – java.util.concurrent sugar 39 Concurrency & parallelism toolkit for Groovy
  57. Closures — resource handling 40 new  File('bible.txt').withReader  {  r  -­‐>

             new  File('out.txt').withWriter  {  w  -­‐>                  r.eachLine  {  line  -­‐>                          if  (line.contains('Groovy'))                                  w  <<  line.toUpperCase()                  }          }   }
  58. Closures — resource handling 40 new  File('bible.txt').withReader  {  r  -­‐>

             new  File('out.txt').withWriter  {  w  -­‐>                  r.eachLine  {  line  -­‐>                          if  (line.contains('Groovy'))                                  w  <<  line.toUpperCase()                  }          }   } Take care of properly opening / closing resources
  59. Closures — custom control structures 41 void  unless(boolean  cond,  Closure

     c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   }
  60. Closures — custom control structures 41 void  unless(boolean  cond,  Closure

     c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } Closure as last argument
  61. Closures — custom control structures 41 void  unless(boolean  cond,  Closure

     c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } Closure as last argument Equivalent to: unless(10<9, {...})
  62. Closures — builders 42 new  MarkupBuilder().html  {      

       head  {                  title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   }
  63. Closures — builders 42 new  MarkupBuilder().html  {      

       head  {                  title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   } Closure
  64. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }

  65. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  66. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  67. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 }
  68. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 }
  69. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 } sell  100.shares  of  GOOG  at  1000.dollars
  70. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 } sell  100.shares  of  GOOG  at  1000.dollars Add properties to numbers
  71. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 } sell  100.shares  of  GOOG  at  1000.dollars Add properties to numbers Nested maps and closures
  72. Closures — higher order functions 43 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google" def  sell(int  nShares)  {
        [of:  {  String  ticker  -­‐>
                [at:  {  int  price  -­‐>
                        println  "Sold  ${nShares}  ${ticker}  at  \$${price}"
                }]
        }]
 } sell  100.shares  of  GOOG  at  1000.dollars Add properties to numbers Nested maps and closures Readable & expressive DSL
  73. Closures — partial application 45 def  mult  =  {  int

     a,  int  b  -­‐>  a  *  b  }   def  multByTwo  =  mult.curry(2)   ! assert  multByTwo(3)  ==  6
  74. Closures — partial application 45 def  mult  =  {  int

     a,  int  b  -­‐>  a  *  b  }   def  multByTwo  =  mult.curry(2)   ! assert  multByTwo(3)  ==  6 Param a is set to 2
  75. Closures — partial application 46 def  join  =  {  String

     left,  String  sep,  String  right  -­‐>          left  +  sep  +  right   }   ! def  prefix  =  join.curry("Groovy")   assert  prefix("  is  ",  "cool")  ==  "Groovy  is  cool"   ! def  suffix  =  join.rcurry("cool")   assert  suffix("Functional",  "  is  ")  ==  "Functional  is  cool"   ! def  amperJoin  =  join.ncurry(1,  "  &  ")   assert  amperJoin("Marion",  "Érine")  ==  "Marion  &  Érine"
  76. Closures — partial application 46 def  join  =  {  String

     left,  String  sep,  String  right  -­‐>          left  +  sep  +  right   }   ! def  prefix  =  join.curry("Groovy")   assert  prefix("  is  ",  "cool")  ==  "Groovy  is  cool"   ! def  suffix  =  join.rcurry("cool")   assert  suffix("Functional",  "  is  ")  ==  "Functional  is  cool"   ! def  amperJoin  =  join.ncurry(1,  "  &  ")   assert  amperJoin("Marion",  "Érine")  ==  "Marion  &  Érine" Left curry by default
  77. Closures — partial application 46 def  join  =  {  String

     left,  String  sep,  String  right  -­‐>          left  +  sep  +  right   }   ! def  prefix  =  join.curry("Groovy")   assert  prefix("  is  ",  "cool")  ==  "Groovy  is  cool"   ! def  suffix  =  join.rcurry("cool")   assert  suffix("Functional",  "  is  ")  ==  "Functional  is  cool"   ! def  amperJoin  =  join.ncurry(1,  "  &  ")   assert  amperJoin("Marion",  "Érine")  ==  "Marion  &  Érine" Left curry by default Right curry
  78. Closures — partial application 46 def  join  =  {  String

     left,  String  sep,  String  right  -­‐>          left  +  sep  +  right   }   ! def  prefix  =  join.curry("Groovy")   assert  prefix("  is  ",  "cool")  ==  "Groovy  is  cool"   ! def  suffix  =  join.rcurry("cool")   assert  suffix("Functional",  "  is  ")  ==  "Functional  is  cool"   ! def  amperJoin  =  join.ncurry(1,  "  &  ")   assert  amperJoin("Marion",  "Érine")  ==  "Marion  &  Érine" Left curry by default Right curry Curry n-th argument
  79. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  }
  80. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  }
  81. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4))
  82. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4))
  83. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4)) def  plus2times3  =  times3  <<  plus2
 assert  plus2times3(3)  ==  15
 assert  plus2times3(5)  ==  times3(plus2(5))
  84. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4)) def  plus2times3  =  times3  <<  plus2
 assert  plus2times3(3)  ==  15
 assert  plus2times3(5)  ==  times3(plus2(5))
  85. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4)) def  plus2times3  =  times3  <<  plus2
 assert  plus2times3(3)  ==  15
 assert  plus2times3(5)  ==  times3(plus2(5)) assert  times3plus2(3)  ==  (times3  >>  plus2)(3)
  86. Closures — composition 48 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4)) def  plus2times3  =  times3  <<  plus2
 assert  plus2times3(3)  ==  15
 assert  plus2times3(5)  ==  times3(plus2(5)) assert  times3plus2(3)  ==  (times3  >>  plus2)(3) Reverse composition
  87. Closures — recursion 50 def  naiveFact  =  {  n  -­‐>

             if  (n  <  2)  n          else  n  *  call(n  -­‐  1)   }   ! assert  naiveFact(10)  ==  3628800
  88. Closures — recursion 50 def  naiveFact  =  {  n  -­‐>

             if  (n  <  2)  n          else  n  *  call(n  -­‐  1)   }   ! assert  naiveFact(10)  ==  3628800 What about naiveFact(1000)?
  89. Closures — recursion 50 def  naiveFact  =  {  n  -­‐>

             if  (n  <  2)  n          else  n  *  call(n  -­‐  1)   }   ! assert  naiveFact(10)  ==  3628800 What about naiveFact(1000)?
  90. Closures — recursion with trampoline 52 def  fact  =  {

     n,  accu  =  1G  -­‐>          if  (n  <  2)  accu          if  fact.trampoline(n  -­‐  1,  n  *  accu)   }.trampoline()   ! assert  fact(1)        ==  1
 assert  fact(3)        ==  1  *  2  *  3   assert  fact(1000)  ==  402387260...
 //  plus  another  2560  digits
  91. Closures — recursion with trampoline 52 def  fact  =  {

     n,  accu  =  1G  -­‐>          if  (n  <  2)  accu          if  fact.trampoline(n  -­‐  1,  n  *  accu)   }.trampoline()   ! assert  fact(1)        ==  1
 assert  fact(3)        ==  1  *  2  *  3   assert  fact(1000)  ==  402387260...
 //  plus  another  2560  digits Not so elegant, needs rewriting
  92. Closures — @TailRecursive 53 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   }
  93. Closures — @TailRecursive 53 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   } Since Groovy 2.3
  94. Closures — @TailRecursive 53 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   } Since Groovy 2.3 Transformation applied to methods, not closures
  95. Closures — Memoization 54 def  plus  =  {  a,  b

     -­‐>  
        sleep  1000
        return  a  +  b     }.memoize()   ! assert  plus(1,  2)  ==  3  //  after  1000ms   assert  plus(1,  2)  ==  3  //  return  immediately   assert  plus(2,  2)  ==  4  //  after  1000ms   assert  plus(2,  2)  ==  4  //  return  immediately
  96. Closures — Memoization 54 def  plus  =  {  a,  b

     -­‐>  
        sleep  1000
        return  a  +  b     }.memoize()   ! assert  plus(1,  2)  ==  3  //  after  1000ms   assert  plus(1,  2)  ==  3  //  return  immediately   assert  plus(2,  2)  ==  4  //  after  1000ms   assert  plus(2,  2)  ==  4  //  return  immediately Also: memoizeAtLeast(10) memoizeAtMost(20) memoizeBetween(10, 20)
  97. Closures — Memoization • Cache the result of previous invocations

    of closures or methods with the same set 
 of argument values 55 def  fib  =  {  n  -­‐>          if  (n  <  2)  1          else  call(n  -­‐  1)  +  call(n  -­‐  2)   }.memoize()   ! assert  fib(10)  ==  89
  98. Memoization 57 import  groovy.transform.*   ! @Memoized   long  fib(long

     n)  {          if  (n  <  2)  1          else  fib(n  -­‐  1)  +  fib(n  -­‐  2)   }   ! println  fib(40)
  99. Memoization 57 import  groovy.transform.*   ! @Memoized   long  fib(long

     n)  {          if  (n  <  2)  1          else  fib(n  -­‐  1)  +  fib(n  -­‐  2)   }   ! println  fib(40) Best applied to side-effect free functions
  100. def  list  =  [1,  3,  5,  7]
      

                   .asImmutable()   ! list  <<  9
  101. Immutability is good! def  list  =  [1,  3,  5,  7]


                         .asImmutable()   ! list  <<  9
  102. Immutability is good! def  list  =  [1,  3,  5,  7]


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException
  103. Immutability is good! def  list  =  [1,  3,  5,  7]


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException Easier to reason about Only one state Can be shared in parallel Suitable for caching ....
  104. Immutability is good! def  list  =  [1,  3,  5,  7]


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException Easier to reason about Only one state Can be shared in parallel Suitable for caching .... What about immutable POJOs/POGOs?
  105. Immutability • Implement immutability 
 by the book ! –

    final class – tuple-style constructor – private final backing fields – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 59
  106. Immutability • Implement immutability 
 by the book ! –

    final class – tuple-style constructor – private final backing fields – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 59 Can be error-prone to write immutable classes oneself!
  107. Immutability • A Person class with – a String name

    – an int age 60 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }!
  108. Immutability • A Person class with – a String name

    – an int age 60 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }! Damn verbose Java!
  109. Immutability • A Person class with – a String name

    – an int age 60 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }! Damn verbose Java! Although it’s also a valid Groovy program!
  110. @Immutable 61 import  groovy.transform.*   ! @Immutable   class  Person

     {          String  name          int  age   }
  111. Groovy allows you to be lazy The compiler will do

    the job for you More concise, more readable code
  112. Groovy allows you to be lazy The compiler will do

    the job for you More concise, more readable code Less stuff to maintain and worry about
  113. @Lazy 64 class  CountryCodes  {          private

     @Lazy  String[]  codes  =                                      ['FR',  'US',  'GB',  /*...*/]   }   ! class  Portfolio  {          @Lazy  List<Position>  positions  =  {                  //  fetch  positions  lazily  from  a  DB          }()   }
  114. @Lazy 64 class  CountryCodes  {          private

     @Lazy  String[]  codes  =                                      ['FR',  'US',  'GB',  /*...*/]   }   ! class  Portfolio  {          @Lazy  List<Position>  positions  =  {                  //  fetch  positions  lazily  from  a  DB          }()   } Lazily allocate and create some collection
  115. @Lazy 64 class  CountryCodes  {          private

     @Lazy  String[]  codes  =                                      ['FR',  'US',  'GB',  /*...*/]   }   ! class  Portfolio  {          @Lazy  List<Position>  positions  =  {                  //  fetch  positions  lazily  from  a  DB          }()   } Lazily allocate and create some collection A lazy computation for an expensive structure with closure logic
  116. Groovy-stream library 65 @Grab('com.bloidonia:groovy-­‐stream:0.5.2')   import  groovy.stream.Stream   ! def

     integers  =  Stream.from  {  x++  }  using  x:  1   def  squares    =  Stream.from  integers  map  {  it  *  it  }   def  first5      =  squares.take(  5  ).collect()   ! assert  first5  ==  [  1,  4,  9,  16,  25  ]   ! def  s  =  Stream.from(  x:1..5,  y:1..3  )                              .filter  {  (x  +  y)  %  (x  +  2)  ==  0  }                              .map  {  x  +  y  }   ! assert  s.collect()  ==  [  3,  4,  5,  6,  7  ]
  117. Groovy-stream library 65 @Grab('com.bloidonia:groovy-­‐stream:0.5.2')   import  groovy.stream.Stream   ! def

     integers  =  Stream.from  {  x++  }  using  x:  1   def  squares    =  Stream.from  integers  map  {  it  *  it  }   def  first5      =  squares.take(  5  ).collect()   ! assert  first5  ==  [  1,  4,  9,  16,  25  ]   ! def  s  =  Stream.from(  x:1..5,  y:1..3  )                              .filter  {  (x  +  y)  %  (x  +  2)  ==  0  }                              .map  {  x  +  y  }   ! assert  s.collect()  ==  [  3,  4,  5,  6,  7  ] Lazy streams and comprehensions
  118. A few other AST transformations • @Delegate – implement the

    delegate design pattern ! • @Canonical – like @Immutable but for mutable data – combines the @TupleConstructor, @EqualsAndHashcode, and @ToString transformations ! • @Singleton – to create singleton objects (potentially lazily) 66
  119. Persistent data structures • No built-in persistent collections — yet

    ! • But works well with existing – Functional Java – pcollections – clj-ds – totallylazy 67
  120. Persistent data structures 68 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.* def  m0  =

     HashTreePMap.from(a:  1,  b:  2)
 assert  m0.size()  ==  2 def  m1  =  m0  +  [c:  3]
 assert  m1.size()  ==  3  &&  m1.c  ==  3
 assert  m0.size()  ==  2
  121. Persistent data structures 68 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.* def  m0  =

     HashTreePMap.from(a:  1,  b:  2)
 assert  m0.size()  ==  2 def  m1  =  m0  +  [c:  3]
 assert  m1.size()  ==  3  &&  m1.c  ==  3
 assert  m0.size()  ==  2
  122. Persistent data structures 68 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.* def  m0  =

     HashTreePMap.from(a:  1,  b:  2)
 assert  m0.size()  ==  2 def  m1  =  m0  +  [c:  3]
 assert  m1.size()  ==  3  &&  m1.c  ==  3
 assert  m0.size()  ==  2 def  m2  =  m0  -­‐  'a'
 assert  m0.size()  ==  2
 assert  m1.size()  ==  3
 assert  m2.size()  ==  1
  123. Monads, Functors, Option, Either… • Groovy doesn’t come built-in with

    monads, option, either, comprehensions, but third-party libraries bring that support ! – Monadologie – Fpiglet – Functional Groovy 70
  124. Monads, Functors, Option, Either… • Groovy doesn’t come built-in with

    monads, option, either, comprehensions, but third-party libraries bring that support ! – Monadologie – Fpiglet – Functional Groovy 70 References available in the «resources» part
  125. ForEach monad comprehension 71 import  static        

     hr.helix.monadologie.MonadComprehension.foreach   ! def  res  =  foreach  {          a  =  takeFrom  {  [1,  2,  3]  }          b  =  takeFrom  {  [4,  5]  }   !        yield  {  a  +  b  }   }   ! assert  res  ==  [5,  6,  6,  7,  7,  8]
  126. • Paul King – Concurrency with GPars • http://slideshare.net/paulk_asert/concurrency-gpars –

    Functional Groovy • http://slideshare.net/paulk_asert/functional-groovy • Andres Almiray – Functional Groovy • http://slideshare.net/aalmiray/functional-groovy-confess • Arturo Herrero – Functional programming with Groovy • http://slideshare.net/arturoherrero/functional-programming-with-groovy • Neal Ford – Functional thinking • http://www.youtube.com/watch?v=7aYS9PcAITQ&feature=youtu.be • Mario Fusco – If you think you can stay away from FP, you are wrong • http://slideshare.net/mariofusco/if-you-think-you-can-stay-away-from-functional-programming-you-are-wrong Resources — Presentations 75
  127. Resources — Interesting projects • GPars — Concurrency & parallelism

    library – http://gpars.codehaus.org/ • Fpiglet — Blog post experiments on combinators, monadic comprehensions – https://code.google.com/p/fpiglet/ – http://rpeszek.blogspot.ch/ • Monadologie — Monad comprehensions library – https://github.com/dsrkoc/monadologie • Groovy Stream — Lazy iterators and streams – http://timyates.github.io/groovy-stream/ • Functional Groovy — Functional Java wrapper – https://github.com/mperry/functionalgroovy 76