grooming with groovy

Post on 02-Jul-2015

2.508 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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

Grooming With Groovy

Come,Code Along!

dhaval.dalal@software-artisan.com

@softwareartisan

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

top related