Functional Groovy

Functional Groovy

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

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

May 15, 2014
Tweet

Transcript

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

    without permission. Guillaume Laforge 
 @glaforge Functional Groovy
  2. Guillaume Laforge Groovy project lead ! @glaforge http://www.google.com/+GuillaumeLaforge http://glaforge.appspot.com

  3. None
  4. 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
  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
 in 2013 3.0

  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',  6),
        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',  6),
        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',  6),
        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',  6),
        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',  6),
        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 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
  82. Closures — Closures vs Java 8 lambdas? 37

  83. 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);
  84. 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);
  85. 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
  86. Closures — map / filter / reduce & GPars 38

  87. Closures — map / filter / reduce & GPars 38

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

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

    import  static  groovyx.gpars.GParsPool.withPool
 
 withPool  {        def  oneStarters  =  (1..30).parallel
                        .map        {  it  **  2  }
                        .filter  {  it  ==~  /1.*/  }
  90. 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]
  91. 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]
  92. 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
 }
  93. 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!
  94. 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»
  95. 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
  96. 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
  97. 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()                  }          }   }
  98. 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
  99. Closures — custom control structures 41 void  unless(boolean  cond,  Closure

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

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

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

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

  105. Closures — higher order functions 43 sell  100.shares  of  GOOG

     at  1000.dollars
  106. Closures — higher order functions 43

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

     }
 Number.metaClass.getDollars  =  {  delegate  }

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

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

     }
 Number.metaClass.getDollars  =  {  delegate  }
 String  GOOG  =  "Google"
  110. 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}"
                }]
        }]
 }
  111. 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}"
                }]
        }]
 }
  112. 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
  113. 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
  114. 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
  115. 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
  116. None
  117. Now what about some curry?

  118. Closures — partial application 45 def  mult  =  {  int

     a,  int  b  -­‐>  a  *  b  }   def  multByTwo  =  mult.curry(2)   ! assert  multByTwo(3)  ==  6
  119. 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
  120. 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"
  121. 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
  122. 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
  123. 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
  124. None
  125. Composition

  126. Closures — composition 48

  127. Closures — composition 48 def  plus2    =  {  it

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

     +  2  }
 def  times3  =  {  it  *  3  }
  129. 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))
  130. 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))
  131. 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))
  132. 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))
  133. 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)
  134. 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
  135. None
  136. Tail recursion!

  137. Closures — recursion 50 def  naiveFact  =  {  n  -­‐>

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

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

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

  142. 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
  143. 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
  144. Closures — @TailRecursive 53 @groovy.transform.TailRecursive   def  fact(n,  accu  =

     1G)  {          if  (n  <  2)  accu          else  fact(n  -­‐  1,  n  *  accu)   }
  145. 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
  146. 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
  147. 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
  148. 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)
  149. 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
  150. None
  151. Compile-time meta-programming to the rescue!

  152. Memoization 57 import  groovy.transform.*   ! @Memoized   long  fib(long

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

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


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


                         .asImmutable()   ! list  <<  9 Throws UnsupportedOperationException
  157. 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 ....
  158. 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?
  159. 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
  160. 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!
  161. 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 + ")";! }! }!
  162. 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!
  163. 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!
  164. @Immutable 61 import  groovy.transform.*   ! @Immutable   class  Person

     {          String  name          int  age   }
  165. None
  166. Groovy allows you to be lazy

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

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

    the job for you More concise, more readable code
  169. 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
  170. None
  171. Speaking of laziness...

  172. @Lazy 64 class  CountryCodes  {          private

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

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

  180. Persistent data structures 68 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.*

  181. Persistent data structures 68 @Grab("org.pcollections:pcollections:2.1.2")
 import  org.pcollections.*

  182. 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
  183. 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
  184. 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
  185. 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
  186. 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
  187. None
  188. That’s probably the part of the talk where you’d need

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

    a brain But I don’t have one
  190. 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
  191. 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
  192. 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]
  193. Part 4 Summary

  194. None
  195. I’m functional!

  196. Part 5 Resources

  197. • 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
  198. 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
  199. Thank you! Q&A