Functional Groovy

Functional Groovy

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

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

October 25, 2013
Tweet

Transcript

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

    without permission. Guillaume Laforge 
 @glaforge Functional Groovy
  2. Guillaume  Laforge Groovy project lead ! @glaforge http://gplus.to/glaforge http://glaforge.appspot.com

  3. None
  4. Les Cast Codeurs http://lescastcodeurs.com/

  5. Part 1
 a Groovy primer

  6. None
  7. Simplify the life of (Java) developers

  8. None
  9. Groovy as a Java superset

  10. It’s so easy to learn! Groovy as a Java superset

  11. None
  12. None
  13. Great for scripting

  14. Great for scripting Fit for Domain- Specific Languages

  15. Great for scripting Fit for Domain- Specific Languages Most seamless

    integration & interoperability wih java!
  16. None
  17. 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          }   }
  18. None
  19. new  MarkupBuilder().html  {          head  {  

                   title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   }
  20. None
  21. move  forward  at  3.km/h Expressive, Concise, Readable

  22. None
  23. None
  24. @RestController   class  App  {          @RequestMapping("/")

             String  home()  {  "Hello  World!"  }   } Speaking of conciseness... A full Spring app in the span of a tweet!
  25. None
  26. AssafeandfastasJava with statictypechecking & compilation

  27. AssafeandfastasJava with statictypechecking & compilation

  28. million 
 downloads
 per year 1.7

  29. None
  30. None
  31. Yup, we’re all using Groovy!

  32. Part 2
 Community

  33. A blossoming Ecosystem

  34. None
  35. None
  36. None
  37. None
  38. GVM

  39. Part 3 Functional

  40. What is a functional language?

  41. 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
  42. None
  43. Closures as first-class citizens of the language

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

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

     b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Closure parameters
  47. 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
  48. 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
  49. 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
  50. Closures — explicit types !28 ! def  intAdder  =  {

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

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

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

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

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

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

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

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

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

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

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

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

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

    ! def  r  =  ints.dropWhile  {  it  <  10  }                          .take(  3  )   ! assert  r  ==  [10,  11,  12]
  68. 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
  69. 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()
  70. 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
  71. 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
  72. Closures — map / filter / reduce !35 3 }

    map filter reduce
  73. Closures — map / filter / reduce !36

  74. Closures — map / filter / reduce !36 @groovy.transform.Immutable
 class

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

     Person  {
        String  name
        int  age
 }
  76. 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)
 ]
  77. 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)
 ]
  78. 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(',  ')
  79. 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(',  ')
  80. 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"
  81. Closures — map / filter / reduce & GPars !37

  82. Closures — map / filter / reduce & GPars !37

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

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

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }
  85. 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]
  86. 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]
  87. 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
 }
  88. 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!
  89. 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
  90. 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
  91. 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()                  }          }   }
  92. 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
  93. Closures — custom control structures !40 void  unless(boolean  cond,  Closure

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

     c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } Closure as last argument
  95. 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, {...})
  96. Closures — builders !41 new  MarkupBuilder().html  {      

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

       head  {                  title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   } Closure
  98. Closures — higher order functions !42

  99. Closures — higher order functions !42 sell  100.shares  of  GOOG

     at  1000.dollars
  100. Closures — higher order functions !42

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

     }
 Number.metaClass.getDollars  =  {  delegate  }

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

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

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  104. 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}"
                }]
        }]
 }
  105. 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}"
                }]
        }]
 }
  106. 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
  107. 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
  108. 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
  109. 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
  110. None
  111. Now what about some curry?

  112. Closures — partial application !44 def  mult  =  {  int

     a,  int  b  -­‐>  a  *  b  }   def  multByTwo  =  mult.curry(2)   ! assert  multByTwo(3)  ==  6
  113. 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
  114. 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"
  115. 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
  116. 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
  117. 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
  118. None
  119. Composition

  120. Closures — composition !47

  121. Closures — composition !47 def  plus2    =  {  it

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

     +  2  }
 def  times3  =  {  it  *  3  }
  123. 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))
  124. 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))
  125. 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))
  126. 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))
  127. 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)
  128. None
  129. Tail recursion!

  130. Closures — recursion !49 def  naiveFact  =  {  n  -­‐>

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

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

             if  (n  <  2)  n          else  n  *  call(n  -­‐  1)   }   ! assert  naiveFact(10)  ==  3628800 What about naiveFact(1000)?
  133. None
  134. Trampoline!

  135. 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
  136. 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
  137. Closures — @TailRecursive !52 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   }
  138. 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
  139. 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
  140. 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
  141. 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
  142. 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)
  143. 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
  144. None
  145. Compile-time meta-programming to the rescue!

  146. Memoization !56 import  groovy.transform.*   ! @Memoized   long  fib(long

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

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


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


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException
  151. 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 ....
  152. 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?
  153. 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
  154. 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!
  155. 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 + ")";! }! }!
  156. 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!
  157. 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!
  158. @Immutable !60 import  groovy.transform.*   ! @Immutable   class  Person

     {          String  name          int  age   }
  159. None
  160. Groovy allows you to be lazy

  161. Groovy allows you to be lazy The compiler will do

    the job for you
  162. Groovy allows you to be lazy The compiler will do

    the job for you More concise, more readable code
  163. 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
  164. None
  165. Speaking of laziness...

  166. @Lazy !63 class  CountryCodes  {          private

     @Lazy  String[]  codes  =                                      ['FR',  'US',  'GB',  /*...*/]   }   ! class  Portfolio  {          @Lazy  List<Position>  positions  =  {                  //  fetch  positions  lazily  from  a  DB          }()   }
  167. @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
  168. @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
  169. 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  ]
  170. 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
  171. 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
  172. Persistent data structures • No built-in persistent collections — yet

    ! • But works well with existing – Functional Java – pcollections – clj-ds – totallylazy !66
  173. Persistent data structures !67

  174. Persistent data structures !67 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.*

  175. Persistent data structures !67 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.*

  176. 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
  177. 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
  178. 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
  179. 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
  180. None
  181. That’s probably the part of the talk where you’d need

    a brain
  182. That’s probably the part of the talk where you’d need

    a brain But I don’t have one
  183. 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
  184. 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
  185. 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]
  186. Part 4 Summary

  187. None
  188. I’m functional!

  189. Part 5 Resources

  190. • 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
  191. 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
  192. Thank you! Q&A