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

Functional Groovy

Functional Groovy

Presentation on the functional aspects of the Groovy programming language, given at the Scala IO 2013 conference in Paris

Guillaume Laforge

October 25, 2013
Tweet

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

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

    without permission. Guillaume Laforge 
 @glaforge Functional Groovy
  2. 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          }   }
  3. new  MarkupBuilder().html  {          head  {  

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

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

  6. 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
  7. 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
  8. Closures — the basics !27 def  adder  =  {  a,

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

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Closure parameters
  10. 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
  11. 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
  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’) Genericity with duck typing & operator overloading Assign a function into a variable Closure parameters
  13. Closures — explicit types !28 ! def  intAdder  =  {

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

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

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

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

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

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

     elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' Variable number of arguments
  20. 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
  21. Closures — default values !31 def  mult  =  {  int

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

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

      def  printer  =  System.out.&println   ! assert  logBase10(10)  ==  1   printer  'abc'
  26. 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
  27. Closures — iterations !33 def  fruits  =  ['apple',  
  

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

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

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

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12]
  31. 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
  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 take(), takeWhile(), drop(), dropWhile()
  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() Returns an iterator
  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 Variants for maps too
  35. Closures — map / filter / reduce !36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 }
  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
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ]
  38. Closures — map / filter / reduce !36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        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',  5),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ')
  40. Closures — map / filter / reduce !36 @groovy.transform.Immutable
 class

     Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        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',  5),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') assert  names  ==  "ERINE,  MARION"
  42. Closures — map / filter / reduce & GPars !37

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {
  43. Closures — map / filter / reduce & GPars !37

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

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

    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]
  46. Closures — map / filter / reduce & GPars !37

    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]
  47. Closures — map / filter / reduce & GPars !37

    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
 }
  48. Closures — map / filter / reduce & GPars !37

    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!
  49. 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 !38
  50. 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 !38 Concurrency & parallelism toolkit for Groovy
  51. Closures — resource handling !39 new  File('bible.txt').withReader  {  r  -­‐>

             new  File('out.txt').withWriter  {  w  -­‐>                  r.eachLine  {  line  -­‐>                          if  (line.contains('Groovy'))                                  w  <<  line.toUpperCase()                  }          }   }
  52. Closures — resource handling !39 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
  53. Closures — custom control structures !40 void  unless(boolean  cond,  Closure

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

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

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

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

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

     }
 Number.metaClass.getDollars  =  {  delegate  }

  59. Closures — higher order functions !42 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  60. Closures — higher order functions !42 Number.metaClass.getShares  =  {  delegate

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  61. Closures — higher order functions !42 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}"
                }]
        }]
 }
  62. Closures — higher order functions !42 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}"
                }]
        }]
 }
  63. Closures — higher order functions !42 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
  64. Closures — higher order functions !42 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
  65. Closures — higher order functions !42 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
  66. Closures — higher order functions !42 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
  67. Closures — partial application !44 def  mult  =  {  int

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

     a,  int  b  -­‐>  a  *  b  }   def  multByTwo  =  mult.curry(2)   ! assert  multByTwo(3)  ==  6 Param a is set to 2
  69. Closures — partial application !45 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"
  70. Closures — partial application !45 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
  71. Closures — partial application !45 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
  72. Closures — partial application !45 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
  73. Closures — composition !47 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  }
  74. Closures — composition !47 def  plus2    =  {  it

     +  2  }
 def  times3  =  {  it  *  3  }
  75. Closures — composition !47 def  plus2    =  {  it

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

     +  2  }
 def  times3  =  {  it  *  3  } def  times3plus2  =  plus2  <<  times3
 assert  times3plus2(3)  ==  11
 assert  times3plus2(4)  ==  plus2(times3(4))
  77. Closures — composition !47 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))
  78. Closures — composition !47 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))
  79. Closures — composition !47 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)
  80. Closures — recursion !49 def  naiveFact  =  {  n  -­‐>

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

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

             if  (n  <  2)  n          else  n  *  call(n  -­‐  1)   }   ! assert  naiveFact(10)  ==  3628800 What about naiveFact(1000)?
  83. Closures — recursion with trampoline !51 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
  84. Closures — recursion with trampoline !51 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
  85. Closures — @TailRecursive !52 @groovy.transform.TailRecursive   def  fact(n,  accu  =

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

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   } https://github.com/jlink/tailrec
  87. Closures — @TailRecursive !52 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   } https://github.com/jlink/tailrec Inclusion in Groovy 2.3
  88. Closures — @TailRecursive !52 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   } https://github.com/jlink/tailrec Inclusion in Groovy 2.3 Transformation applied to methods, not closures
  89. Closures — Memoization !53 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
  90. Closures — Memoization !53 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)
  91. Closures — Memoization • Cache the result of previous invocations

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

     n)  {          if  (n  <  2)  1          else  fib(n  -­‐  1)  +  fib(n  -­‐  2)   }   ! println  fib(40)
  93. Memoization !56 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
  94. def  list  =  [1,  3,  5,  7]
      

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


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


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException
  97. 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 ....
  98. 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?
  99. 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 – ... !58
  100. 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 – ... !58 Can be error-prone to write immutable classes oneself!
  101. Immutability • A Person class with – a String name

    – an int age !59 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 + ")";! }! }!
  102. Immutability • A Person class with – a String name

    – an int age !59 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!
  103. Immutability • A Person class with – a String name

    – an int age !59 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!
  104. @Immutable !60 import  groovy.transform.*   ! @Immutable   class  Person

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

    the job for you More concise, more readable code
  106. 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
  107. @Lazy !63 class  CountryCodes  {          private

     @Lazy  String[]  codes  =                                      ['FR',  'US',  'GB',  /*...*/]   }   ! class  Portfolio  {          @Lazy  List<Position>  positions  =  {                  //  fetch  positions  lazily  from  a  DB          }()   }
  108. @Lazy !63 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
  109. @Lazy !63 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
  110. Groovy-stream library !64 @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  ]
  111. Groovy-stream library !64 @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
  112. 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) !65
  113. Persistent data structures • No built-in persistent collections — yet

    ! • But works well with existing – Functional Java – pcollections – clj-ds – totallylazy !66
  114. Persistent data structures !67 @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
  115. Persistent data structures !67 @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
  116. 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 !69
  117. 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 !69 References available in the «resources» part
  118. ForEach monad comprehension !70 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]
  119. • 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 !74
  120. 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 !75