grooming with groovy

84
Grooming With Groovy Come,Code Along! [email protected] @softwareartisan

Upload: dhaval-dalal

Post on 02-Jul-2015

2.508 views

Category:

Technology


0 download

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

Page 1: Grooming with Groovy

Grooming With Groovy

Come,Code Along!

[email protected]

@softwareartisan

Page 2: Grooming with Groovy

Hello World…

Concise - convey more in less

Groovy Classes Are Java Classes

Everything is an object.

Page 3: Grooming with Groovy

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

Page 4: Grooming with Groovy

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

Page 5: Grooming with Groovy

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.

Page 6: Grooming with Groovy

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

Page 7: Grooming with Groovy

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

Page 8: Grooming with Groovy

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.

Page 9: Grooming with Groovy

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/

Page 10: Grooming with Groovy

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?

Page 11: Grooming with Groovy

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

Page 12: Grooming with Groovy

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

Page 13: Grooming with Groovy

WeaklyTyped

StronglyTyped

StaticallyTyped C/C++ Haskell/Java/

C#

DynamicallyTyped

JavaScript/Perl

Groovy/Ruby/Python

Source: http://c2.com/cgi/wiki?TypingQuadrant

The Typing Quadrant

Page 14: Grooming with Groovy

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

Page 15: Grooming with Groovy

Match found!Pattern from String

use operator ~

Determine a match =~

For exact match ==~

Regex Readability

Prefer Slashy strings over normal ones

Page 16: Grooming with Groovy

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 (?.)

Page 17: Grooming with Groovy

Methods and Parameters

Properties (Getters and Setters)

Trailing Optional Parameters

Positional parameters - @TupleConstructor

@Cannonical = @EqualsAndHashCode, @ToString and @TupleConstructor

Page 18: Grooming with Groovy

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.

Page 19: Grooming with Groovy

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.

Page 20: Grooming with Groovy

A Peek at GDKProducing and Consuming XML

Spawning Processes

Using Files

Using Configuration

Avoid properties hell.

Using SQL

Page 21: Grooming with Groovy

Compose behaviours, Don’t inherit

@Delegate

@Mixin (< 2.3)

Traits (>= 2.3, @Mixin deprecated)

trait -> Language keyword, not an AST transformation

Page 22: Grooming with Groovy

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.

Page 23: Grooming with Groovy

Closures Objects = data + behavior, whereas Closures = behavior.

Convert Methods to Closures.

Using the object.&method

Static, Instance or in Script methods.

The with closure.

Page 24: Grooming with Groovy

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.

Page 25: Grooming with Groovy

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!

Page 26: Grooming with Groovy

More on Closures Curried Closures

Spice that helps us re-shape and re-purpose!

curry, rcurry

Composing Closures

The way to tackle complexity!

>> and <<

Page 27: Grooming with Groovy

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

Page 28: Grooming with Groovy

Hello Collections…Lists - [] , Maps - [:], Ranges - (a..b), Sets

Tuples

Iterate each element.

each, reverseEach, eachWithIndex

Transform each element.

collect, collectNested, collectMany and collectEntries

Page 29: Grooming with Groovy

Collection OperationsRetrieve Elements that satisfy certain criterion

find, findAll

Transform using predicate (collect + ~find)

findResult(s)

Combine adjacent elements

sum, inject, min, max

Page 30: Grooming with Groovy

Collection Ops

Queries

contains, count, any, every

Mutating Operations

reverse, removeAll, retainAll

Generators

collate, permutations, combinations, subsequences, transpose

Page 31: Grooming with Groovy

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

Page 32: Grooming with Groovy

Spreading Collections

The Spread-dot Operator (*.)

Array and List as varargs

The Spread Operator (*)

Map as an Interface.

Very useful when Stubbing

Page 33: Grooming with Groovy

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.

Page 34: Grooming with Groovy

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

Page 35: Grooming with Groovy

Immutability

Immutability and Pure functions make it easier to reason about programs

Use @Immutable to make a class immutable

Page 36: Grooming with Groovy

Immutability

Collection APIs

Prefer Immutable version

Over Mutable version

Adding + <<, addAll

Removing - removeAll

Sorting sort(false) sort()

Reversing reverse() reverse(false)

Page 37: Grooming with Groovy

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

Page 38: Grooming with Groovy

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.

Page 39: Grooming with Groovy

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

Page 40: Grooming with 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

Page 41: Grooming with 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

Page 42: Grooming with 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.

Page 43: Grooming with Groovy

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

Page 44: Grooming with Groovy

Stream with head

and lazy

evaluated tail

As Streams are immutable we can structurally share the earlier stream.

Page 45: Grooming with Groovy

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

Page 46: Grooming with Groovy

Introduce Empty Stream

Page 47: Grooming with Groovy

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, ?]

Page 48: Grooming with Groovy

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)

Page 49: Grooming with Groovy

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, ?]

Page 50: Grooming with Groovy

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"))

Page 51: Grooming with Groovy

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]

Page 52: Grooming with Groovy

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() // []

Page 53: Grooming with Groovy

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

Page 54: Grooming with Groovy

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

Page 55: Grooming with Groovy

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

Page 56: Grooming with Groovy

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]

Page 57: Grooming with Groovy

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

Page 58: Grooming with Groovy

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]

Page 59: Grooming with Groovy

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

Page 60: Grooming with Groovy

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() // []

Page 61: Grooming with Groovy

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]

Page 62: Grooming with Groovy

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]

Page 63: Grooming with Groovy

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]

Page 64: Grooming with Groovy

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]

Page 65: Grooming with Groovy

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]

Page 66: Grooming with Groovy

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

Page 67: Grooming with Groovy

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()

Page 68: Grooming with Groovy

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

Page 69: Grooming with Groovy

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 }

Page 70: Grooming with Groovy

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]

Page 71: Grooming with Groovy

Find first 6 primes using the Sieve of Eratosthenes

Hint: Use Stream created earlier

http://world.mathigon.org/Prime_Numbers

Page 72: Grooming with Groovy

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]

Page 73: Grooming with Groovy

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.

Page 74: Grooming with Groovy

Memoized Tail

Page 75: Grooming with Groovy

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]

Page 76: Grooming with Groovy

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]}

Page 77: Grooming with Groovy

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]

Page 78: Grooming with Groovy

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

Page 79: Grooming with Groovy

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]

Page 80: Grooming with Groovy

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]

Page 81: Grooming with Groovy

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]]

Page 82: Grooming with Groovy

Reca

p

Page 83: Grooming with Groovy

Thank-You!

Stream implementation shown in these slides is available on:https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.groovy

Page 84: Grooming with 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