grooming with groovy
DESCRIPTION
Overview of Groovy language features and lead to Functional Programming in Groovy. As this is a code along session backed by this presentation, code examples are not include here as I code them live.TRANSCRIPT
Hello World…
Concise - convey more in less
Groovy Classes Are Java Classes
Everything is an object.
FlexibilityDynamic Language
Program extension at runtime by altering behaviour and structure of objects/types - “Open Classes”
Code synthesis
Add code at runtime
As opposed to manipulating byte code in Static Languages like Java.
Groovy’s extension to JDK is called GDK
UncertaintyPimp(extend) my Library
Extending the class - additive operation.
Monkey Patching
Redefining things again - Don’t cause fires!
Break glass in case of fire - reach out for this meta-programming hammer only then.
Adding != Redefining
Typing 101Type?
How do I interpret data in that block of memory and what operations can I perform on it?
Safety?
Any attempt to misinterpret data is caught at compile time OR generates a well-specified error at runtime.
Dynamic & Static TypingStatically Typed Language
Type of every expression can be determined by static program analysis (before it runs).
Compile-time type checking.
Align types naturally or by casting.
Dynamically Typed
Runtime type checking, not compile-time checking
Dynamic Typing
Infers Type based on context.
Numbers and Decimals
We rely on static typing for code safety, but it still can fail us.
Generics
ArrayStoreException
Dynamic & Static Typing
Optional & Duck TypingOptionally Typed
Specify when you need, and leave it to Groovy to figure it out where you don’t care.
Duck Typing
Don’t need a type to invoke a method on object. If its there, you can invoke it
Objects behave as demanded of them in a given context, rather than ensuring that they are of a specific type.
You can treat it like a dog or even god.
Ram (Static) Balram (Dynamic)Static typing catches bugs with the compiler and keeps you out of trouble.
Static typing only catches some bugs, and you can’t trust the compiler to do your testing
Static languages are easier to read because they’re more explicit about what the code does.
Dynamic languages are easier to read because you write less code.
At least I know that the code compiles.
Just because the code compiles doesn’t mean it runs.
I trust the static typing to make sure my team writes good code.
The compiler doesn’t stop you from writing bad code.
Debugging an unknown object is impossible.
Debugging overly complex object hierarchies is unbearable.
Compiler bugs happen at midmorning in my office; runtime bugs happen at midnight for my customers.
There’s no replacement for testing, and unit tests find more issues than the compiler ever could.
Source: http://www.smashingmagazine.com/2013/04/18/introduction-to-programming-type-systems/
Use correct typing to solve the problem at hand
Doesn’t matter if you have to use more than one at a given time.
Use Static typing where possible, Dynamic typing when needed - Eric Meijer
Avoid Polarization
Like a good manager, play on strengths of each :-)
Dynamic or Static Typing?
Weakly TypedThe type of a value depends on how it is used.
Can mix operations between mismatched types without explicit conversion
C lets you define object types as structures, but it doesn’t do much to enforce or remember them. C automatically converts between many types.
http://c2.com/cgi/wiki?WeakAndStrongTyping
Strongly Typed LanguageA Language in which all expressions are type consistent is Strongly Typed.
A value has a type and that type cannot change. What you can do to a value depends on the type of the value.
Can’t mix operations between mismatched types, if you do, the runtime catches you. You must convert explicitly.
But, being liberal with type conversions doesn't really mean the same as saying that the language is weakly typed!
http://c2.com/cgi/wiki?WeakAndStrongTyping
WeaklyTyped
StronglyTyped
StaticallyTyped C/C++ Haskell/Java/
C#
DynamicallyTyped
JavaScript/Perl
Groovy/Ruby/Python
Source: http://c2.com/cgi/wiki?TypingQuadrant
The Typing Quadrant
Strings in GroovyLiteral Strings
String Interpolation
GString - Nah, stop fantasising!
Allow in-place evaluation of expressions…and for that you have to use the $ ;-)
Multi-line String
Lazy Evaluation
Slashy Strings
Match found!Pattern from String
use operator ~
Determine a match =~
For exact match ==~
Regex Readability
Prefer Slashy strings over normal ones
Truth & Difficult Situations
Groovy Truth
Dealing with Exceptions
No force to catch Java’s Checked Exceptions
Dealing with Absent Values
null is an object, try null.getClass() and toString()
null-safe operator (?.)
Methods and Parameters
Properties (Getters and Setters)
Trailing Optional Parameters
Positional parameters - @TupleConstructor
@Cannonical = @EqualsAndHashCode, @ToString and @TupleConstructor
Hello Operator!
Preserve semantics when using Operator overloading.
When someone asks you - what does this operator do? You know the answer :-)
Use it to harmonise with the domain/context
Typically very nice for value objects.
Multi-MethodsAlso called as Multiple Dispatch.
Dispatch based on type of the receiver.
Example, Java/C#/Scala
and based on Run-time Type of the arguments, not on Compile-time type (as in Java).
Example, Clojure
The equals method.
A Peek at GDKProducing and Consuming XML
Spawning Processes
Using Files
Using Configuration
Avoid properties hell.
Using SQL
Compose behaviours, Don’t inherit
@Delegate
@Mixin (< 2.3)
Traits (>= 2.3, @Mixin deprecated)
trait -> Language keyword, not an AST transformation
TraitsLike interfaces, they cannot be instantiated.
However, unlike interfaces, they can have implementation and state.
Can contain abstract methods.
Classes can inherit multiple traits or implement at run-time.
Trait can extend another trait and implement interfaces as well.
Methods are “real” and visible from Java as well.
Closures Objects = data + behavior, whereas Closures = behavior.
Convert Methods to Closures.
Using the object.&method
Static, Instance or in Script methods.
The with closure.
Closures in ActionAOP around style using Closures.
Loan My Resource - like try-with-resources in Java.
Closure as an interface.
Query Closure to find
maximumNumberOfParameters
parameterTypes
and alter the response accordingly - polymorphic closures.
More on Closures Memoized Closures and @Memoized methods
Remember me and I’ll help you!
Trampolined Closures and @TailRecursive methods
Don’t jump that high, but instead jump many times!
More on Closures Curried Closures
Spice that helps us re-shape and re-purpose!
curry, rcurry
Composing Closures
The way to tackle complexity!
>> and <<
When to use Closures?Eliminating Dependencies
Esp. on single method interfaces
Building Declarative APIs and Preserving encapsulation.
External iterators or getters expose innards of an aggregate, instead use internal iterators and call closure.
Building DSLs
Using HOFs it becomes easy to construct DSLs
Lazy Evaluation
Hello Collections…Lists - [] , Maps - [:], Ranges - (a..b), Sets
Tuples
Iterate each element.
each, reverseEach, eachWithIndex
Transform each element.
collect, collectNested, collectMany and collectEntries
Collection OperationsRetrieve Elements that satisfy certain criterion
find, findAll
Transform using predicate (collect + ~find)
findResult(s)
Combine adjacent elements
sum, inject, min, max
Collection Ops
Queries
contains, count, any, every
Mutating Operations
reverse, removeAll, retainAll
Generators
collate, permutations, combinations, subsequences, transpose
Collection OpsSet-Style Operations
intersect, disjoint, plus (union)
Distinct and Order By
unique, sort
Partition Collection to Map
groupBy, groupEntriesBy, countBy
split and join
drop, take, dropWhile, takeWhile
Spreading Collections
The Spread-dot Operator (*.)
Array and List as varargs
The Spread Operator (*)
Map as an Interface.
Very useful when Stubbing
FP in GroovyLike in other languages Java/C#/Ruby etc…, in-place mutation is a standing invitation in Groovy too!
Its hard to avoid falling into that trap.
One has to work hard to bring immutability.
A change to Immutable Object produces another Immutable Object
Promotes caching of objects - Flyweights.
Promotes concurrent operations.
FP in GroovyUse Expressions wherever possible
Statements effect change by mutation thus encourage mutability
Expressions evaluate and return value thus encourage immutability
Use Pure Functions wherever possible
Order of program evaluation
Referential Transparency
Memoization
Immutability
Immutability and Pure functions make it easier to reason about programs
Use @Immutable to make a class immutable
Immutability
Collection APIs
Prefer Immutable version
Over Mutable version
Adding + <<, addAll
Removing - removeAll
Sorting sort(false) sort()
Reversing reverse() reverse(false)
Lazy EvaluationLazy Evaluation (by-name)
Don’t compute until actually required.
Lazy Evaluation (by-need)
Store the result of computation for future use and avoid re-computation
@Lazy AST Transformation
Needs to be initialised at the time of declaration
Being Lazy is efficientLazy defaults on Lists
Lazy Sequences
A sequence whose tail is evaluated only someone wants to know its value.
Materialise on demand to avoid unnecessary computation of tail.
Lets implement Immutable Stream
Stream implementation shown in these slides is available on:https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.groovy
Lets implement Immutable Stream
How can we implement a lazy List in Groovy?
1 ?
Stream implementation shown in these slides is available on:https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.groovy
Lets implement Immutable Stream
How can we implement a lazy List in Groovy?
By using closure on the tail of the list.
Additionally for performance - use Memoization on that closure.
Evaluate tail
1 ?
1 *
2 ?
Stream implementation shown in these slides is available on:https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.groovy
Ingredients for Lazy Evaluation
Groovy uses eager evaluation for method arguments.
Args are evaluated before passing to the method.
To delay the evaluation of method arguments (lazy), wrap them in Closure
Invoke the closure explicitly in the method when we need the evaluation result.
Ingredients for Lazy Evaluation
Wrap in Closure for Lazy Calls
for storing result of evaluation and avoiding
computation again, performance optimisation
CAVEAT: Only for
pure functions!
Evaluation on Closure
Call
Stream with head
and lazy
evaluated tail
As Streams are immutable we can structurally share the earlier stream.
Stream(T head, Stream<T> tail) { _head = head _tail = {-> tail}.memoize()}
new Stream<Integer>(1, new Stream(null, null))
Stream(T head, Closure tail) { _head = head _tail = tail.memoize()}
new Stream<Integer>(1, {-> new Stream(null, {-> null})})
Causes Immediate Evaluation - Eager
Hence wrap the tail stream in a Closure at the time of Stream Creation - Lazy
Introduce Empty Stream
Define toString String toString() { if (isEmpty()) ‘[]’ else “[${head()}, ?]” }} // End of Stream class
def empty = Stream.Emptyprintln empty // []
def singleton = new Stream(1, {-> empty})println singleton // [1, ?]
def couple = new Stream(2, {-> singleton})println couple // [2, ?]
def leftShift(T element) { new Stream(element, {-> this}) }} // End of Stream class
def stream = Stream.Empty << 1 << 2println stream // [2, ?]println stream.head() // 2println stream.tail() // [1, ?]println stream.tail().tail() // []
Consing to Stream
Prepend (Cons)
Construct a Stream from few elements static def of(T ...ts) { def stream = Stream.Empty ts.reverseEach { stream = stream << it } stream }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)println stream // [1, ?]
Construct a Stream from Range
static def range(Range range, Integer step) { Stream.of(*range.step(step)) }} // End of Stream class
println Stream.range('a'..'z')println Stream.range(‘A'..'Z', 2)println Stream.range(-10..5)println Stream.range(new Date("12-Dec-2012")..new Date("19-Jan-2013"))
Force entire Stream to Evaluate
def force() { if (isEmpty()) [] else [head()] + tail().force() }} // End of Stream class
def empty = Stream.Emptyprintln empty // []
def stream = Stream.of(1, 2, 3, 4)println stream.force() // [1, 2, 3, 4]
Drop first few andtake the rest
def drop(howMany) { if (isEmpty() || howMany <= 0) this else tail().drop(howMany - 1) }} // End of Stream class
def empty = Stream.Emptyprintln empty.drop(2).force() // []
def stream = Stream.of(1, 2, 3, 4)println stream.drop(2) // [3, ?]println stream.drop(2).force() // [3, 4]println stream.drop(9).force() // []
stream as a function of its index
def call(index) { def list = take(index + 1).force() if (index > list.size() - 1) throw new Exception(“Index $index out of bounds”) list[index] }} // End of Stream class
def empty = Stream.Emptyprintln empty(2) // Exception: Index 2 out of bounds
def stream = Stream.of(1, 2, 3, 4)println stream(0) // 1println stream(3) // 4println stream(4) // Exception: Index 4 out of bounds
Combining Adjacent Elements Using fold
def fold(acc, Closure fn) { if (isEmpty()) acc else tail().fold(fn(acc, head()), fn) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)// Sum of all elementsprintln stream.fold(0) { acc, elem -> acc + elem } // 10
// Product of all elementsprintln stream.fold(1) { acc, elem -> acc * elem } // 24
TailRecursive fold @TailRecursive def fold(stream = this, acc, Closure fn) { if (stream.isEmpty()) acc else fold(stream.tail(), fn(acc, stream.head()), fn) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)// Sum of all elementsprintln stream.fold(0) { acc, elem -> acc + elem } // 10
// Product of all elementsprintln stream.fold(1) { acc, elem -> acc * elem } // 24
Express force in terms of fold
But why do we do this?
def force() { fold([]) { acc, elem -> acc + elem } }} // End of Stream class
def empty = Stream.Emptyprintln empty // []
def stream = empty << 1 << 2 << 3 << 4println stream.force() // [4, 3, 2, 1]
force() [4, ?][4] + force() [3, ?][4] + [3] + force() [2, ?][4] + [3] + [2] + force() [1, ?][4] + [3] + [2] + [1] + force() [][4] + [3] + [2] + ([1] + [])[4] + [3] + ([2] + [1])[4] + ([3] + [2, 1])[4] + [3, 2, 1][4, 3, 2, 1]
fold [4, ?] []fold [3, ?] [4]fold [2, ?] [4, 3]fold [1, ?] [4, 3, 2]fold [] [4, 3, 2, 1][4, 3, 2, 1]
Shape of the process becomes iterative after expressing it in terms of fold.
Earlier force implementation was a recursive process.
Shapes of ProcessesBuild-up on stack like this causes it to overflow
State passed as parameter
State stored on stack
Reverse def reverse() { fold(Stream.Empty) { stream, elem -> stream << elem } }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)println stream.reverse() // [4, ?]println stream.reverse().force() // [4, 3, 2, 1]
TailRecursive foldN
@TailRecursive def foldN(stream = this, howMany, acc, Closure fn) { if (stream.isEmpty() || howMany <= 0) acc else foldN(stream.tail(), howMany - 1, fn(acc, stream.head()), fn) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)// Sum of 2 elementsprintln stream.foldN(2, 0) { acc, elem -> acc + elem } // 3
// Product of 2 elementsprintln stream.foldN(2, 1) { acc, elem -> acc * elem } // 2
Express drop in terms of foldN
def drop(howMany) { foldN(howMany, this) { stream, elem -> stream.tail() } }} // End of Stream class
def empty = Stream.Emptyprintln empty.drop(2).force() // []
def stream = Stream.of(1, 2, 3, 4)println stream.drop(2) // [3, ?]println stream.drop(2).force() // [3,4]println stream.drop(9).force() // []
Filter elements by criteria
def filter(Closure predicate) { if (isEmpty()) Stream.Empty else if (predicate(head())) new Stream(head(), {-> tail().filter(predicate)}) else tail().filter(predicate) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)println stream.filter { it % 2 == 0 } // [2, ?]println stream.filter { it % 2 != 0 }.force() // [1, 3]
Take while predicate holds
def takeWhile(Closure predicate) { if (isEmpty()) Stream.Empty else if (predicate(head())) new Stream(head(), {-> tail().takeWhile(predicate)}) else Stream.Empty }} // End of Stream class
def empty = Stream.Emptyprintln empty.takeWhile { it == 1 } // []def stream = Stream.of(1, 1, 1, 2, 1, 2)println stream.takeWhile { it == 1 } // [1, ?]println stream.takeWhile { it == 1 }.force() // [1, 1, 1]
Drop while predicate holds
def dropWhile(Closure predicate) { if (isEmpty()) Stream.Empty else if (predicate(head())) tail().dropWhile(predicate) else this }} // End of Stream class
def empty = Stream.Emptyprintln empty.dropWhile { it == 1 } // []
def stream = def stream = Stream.of(1, 1, 1, 2, 1, 2)println stream.dropWhile { it == 1 } // [2, ?]println stream.dropWhile { it == 1 }.force() // [2, 1, 2]
Transform each Element
def map(Closure fn) { if (isEmpty()) Stream.Empty else new Stream(fn(head()), {-> tail().map(fn)}) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)println stream.map { it * 2 } // [1, ?]println stream.map { it * it }.force() // [1, 4, 9, 16]
Transform using Predicate
def collect(Closure predicate) { if (isEmpty()) Stream.Empty else if (predicate(head())) new Stream(predicate(head()), {-> tail().collect(predicate)}) else tail().collect(predicate) }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)def squaredEvens = stream.collect { if (it % 2 == 0) it * it // Square Evens, leave Odds else null // Groovy truth expression that evaluates to false} println squaredEvens // [4, ?]println squaredEvens.force() // [4, 16]
Iterate each element def forEach(Closure fn) { if (isEmpty()) Stream.Empty else { fn(head()) tail().forEach(fn) } }} // End of Stream class
def stream = Stream.of(1, 2, 3, 4)stream.forEach { print it } // 1234
Non-Terminating Recursion
static def generate(Closure fn) { new Stream(fn(), {-> generate(fn)}) }} // End of Stream class
def rnd = new java.util.Random()def stream = Stream.generate { rnd.nextInt(10) }println stream.take(5).force()
Non-Terminating Recursion
static def iterate(initial, Closure fn) { new Stream(initial, {-> iterate(fn(initial), fn) }) }
static def from(n) { iterate(n) { it + 1 } }} // End of Stream class
Infinitely LazyStreams, unlike lists (eager), are lazy.
Streams, unlike lists (finite), are infinite.
They have a starting point, but no end.
// The below will not terminateStream.from(4).forEach { println it }
Finite from InfiniteTake what we are interested in and leave the rest in ether!
def numbersFrom4 = Stream.from(4)println numbersFrom4.take(2).force() // [4, 5]println numbersFrom4.take(5).force() // [4, 5, 6, 7, 8]
Find first 6 primes using the Sieve of Eratosthenes
Hint: Use Stream created earlier
http://world.mathigon.org/Prime_Numbers
Sieve
def sieve(s) { def first = s.head() def rest = s.tail().filter { it % first != 0 } new Stream(first, {-> sieve(rest)})}
def primes = sieve(Stream.from(2)).take(6)println primes.force() // [2, 3, 5, 7, 11, 13]
Non-Memoized Tail
Serious Performance hit as tail is wrapped in closure and if when called several times the stream is recomputed each time upon call.
Memoized Tail
Zip two Streams def zip(Stream<T> that) { if (isEmpty() || that.isEmpty()) Stream.Empty else new Stream(new Tuple(head(), that.head()), {-> tail().zip(that.tail()) }) }} // End of Stream class
def s1 = Stream.of(0, 1, 2)def s2 = Stream.of(4, 5, 6)def zipped = s1.zip(s2)println zipped // [[0, 4], ?]println zipped.tail() // [[1, 5], ?]println zipped.force() // [0, 4, 1, 5, 2, 6]println zipped.force().collate(2) // [[2, 6], [1, 5], [0,4]]// Sum of 2 streamsprintln zipped.map { def (first, second) = it first + second}.force() // [4, 6, 8]
Zip with Index
def zipWithIndex() { zip(from(0)) }} // End of Stream class
def stream = Stream.of('a', 'b', 'c', 'd')stream.zipWithIndex().forEach { print it // [a, 0][b, 1][c, 2][d, 3]}
def fibonacci(s) { def next = s.zip(s.tail()).map { def (first, second) = it first + second } def rest = s << next.head() new Stream(s.head(), {-> fibonacci(rest)})}
First 10 Fibonacci Nos.
https://www.haskell.org/tutorial/functions.html
def stream = Stream.from(0).take(2)println stream.force() // [0, 1]// Start with seed elements 0 and 1println fibonacci(stream).take(10).force()// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
def fibonacci(n) { Stream.iterate([0, 1]) { def (first, second) = it [second, first + second] }.map { def (first, second) = it first }.take(n)}
println fibonacci(10).force()// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
First 10 Fibonacci Nos.
Definition as suggested by Christopher Grande
Split def split(Closure predicate) { fold([Stream.Empty, Stream.Empty]) { streams, elem -> def (yays, nays) = streams if (predicate(elem)) [yays << elem, nays] else [yays, nays << elem] } }} // End of Stream class
def stream = Stream.from(0).take(10)def (evens, odds) = stream.split { it % 2 == 0 }println odds.force() // [8, 6, 4, 2, 0]println evens.force() // [9, 7, 5, 3, 1]
Concat
def leftShift(Stream<?> other) { if (isEmpty()) other else new Stream(head(), {-> tail() << other}) }} // End of Stream class
def nums = Stream.of(1, 2, 3)def chars = Stream.of('a', 'b', 'c')println ((nums << chars).force()) // [1, 2, 3, a, b, c]
flatMap // fn: T -> Stream<U> def flatMap(Closure fn) { if (isEmpty()) Stream.Empty else fn(head()) << tail().flatMap(fn) }} // End of Stream class
def nums = Stream.of(1, 2)
println nums.flatMap { it -> cs }.force() // [a, b, a, b]println nums.flatMap { n -> Stream.of('a', 'b').map { c -> [c, n] } }.force() // [[a, 1], [b, 1], [a, 2], [b, 2]]
Reca
p
Thank-You!
Stream implementation shown in these slides is available on:https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.groovy
ReferencesProgramming Groovy
Venkat Subramaniam
Groovy In Action
Dierk König
Structure and Interpretation of Computer Programs
Abelson, Sussman and Sussman
Functional Prog. Coursera Course
Martin Odersky
Functional Prog. with Groovy
Arturo Herrero
On Understanding Types, Data Abstraction & Polymorphism
Luca Cardelli, Peter Wegner