Introduction to Clojure Concurrency (and data structures)

Similar documents
Persistent Data Structures and Managed References

Introduction Basics Concurrency Conclusion. Clojure. Marcel Klinzing. December 13, M. Klinzing Clojure 1/18

Clojure Lisp for the Real #clojure

Seminar on Languages for Scientific Computing Aachen, 6 Feb Navid Abbaszadeh.

The New Java Technology Memory Model

Clojure Concurrency Constructs. CSCI 5828: Foundations of Software Engineering Lecture 12 10/02/2014

Identity, State and Values

The Curious Clojureist

CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections. Tyler Robison Summer 2010

Clojure. A Dynamic Programming Language for the JVM. Rich Hickey

Compilers. 8. Run-time Support. Laszlo Böszörmenyi Compilers Run-time - 1

Clojure Lisp for the Real clojure.com

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

Tackling Concurrency With STM. Mark Volkmann 10/22/09

Tackling Concurrency With STM

+ Today. Lecture 26: Concurrency 3/31/14. n Reading. n Objectives. n Announcements. n P&C Section 7. n Race conditions.

A Pragmatic Case For. Static Typing

Multi-core Parallelization in Clojure - a Case Study

What if Type Systems were more like Linters?

Who am I? Harlan Iverson. Programming enthusiast. Seeker of truth. Imperfect. I'll be wrong about some things. Please correct me if you can.

Stuart

CSE 373: Data Structures and Algorithms

Mutable Data Types. Prof. Clarkson Fall A New Despair Mutability Strikes Back Return of Imperative Programming

Kotlin for Android Developers

Synchronization SPL/2010 SPL/20 1

Types for References, Exceptions and Continuations. Review of Subtyping. Γ e:τ τ <:σ Γ e:σ. Annoucements. How s the midterm going?

It turns out that races can be eliminated without sacrificing much in terms of performance or expressive power.

A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 5 Programming with Locks and Critical Sections

Programming Kotlin. Familiarize yourself with all of Kotlin s features with this in-depth guide. Stephen Samuel Stefan Bocutiu BIRMINGHAM - MUMBAI

Thread Safety. Review. Today o Confinement o Threadsafe datatypes Required reading. Concurrency Wrapper Collections

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

CSE341: Programming Languages Lecture 11 Type Inference. Dan Grossman Spring 2016

Applied Unified Ownership. Capabilities for Sharing Across Threads

CSE 341, Spring 2011, Final Examination 9 June Please do not turn the page until everyone is ready.

CS 360 Programming Languages Interpreters

Clojure. A (not-so-pure) functional approach to concurrency. Paolo Baldan Linguaggi per il Global Computing AA 2016/2017

So on the survey, someone mentioned they wanted to work on heaps, and someone else mentioned they wanted to work on balanced binary search trees.

Priming Java for Speed

Understanding Hardware Transactional Memory

Clojure is. A dynamic, LISP-based. programming language. running on the JVM

Introduction to Locks. Intrinsic Locks

Lecture 8: Summary of Haskell course + Type Level Programming

Stop coding Pascal. Saturday, April 6, 13

CSCI-GA Scripting Languages

Clojure. A Dynamic Programming Language for the JVM. (and CLR) Rich Hickey

CMSC 433 Section 0101 Fall 2012 Midterm Exam #1

Michiel DomCode, May 26th 2015

CS 2505 Computer Organization I Test 1. Do not start the test until instructed to do so!

CMSC 330: Organization of Programming Languages

3/25/14. Lecture 25: Concurrency. + Today. n Reading. n P&C Section 6. n Objectives. n Concurrency

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Java Platform Concurrency Gotchas

Data Structures in Functional Languages

Introduction to Concurrent Software Systems. CSCI 5828: Foundations of Software Engineering Lecture 08 09/17/2015

Parsing Scheme (+ (* 2 3) 1) * 1

05. SINGLETON PATTERN. One of a Kind Objects

Races. Example. A race condi-on occurs when the computa-on result depends on scheduling (how threads are interleaved)

MRI Internals. Koichi Sasada.

Clojure & Incanter for Java Programmers

Concurrency & Parallelism. Threads, Concurrency, and Parallelism. Multicore Processors 11/7/17

Mutation. COS 326 David Walker Princeton University

.consulting.solutions.partnership. Clojure by Example. A practical introduction to Clojure on the JVM

Symbols. abstractions, implementing, 270 through indirection, 77 with macros, 183 abstract syntax tree (AST), 149

Programming Languages and Techniques (CIS120)

Functional Programming in Java. CSE 219 Department of Computer Science, Stony Brook University

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

CSE 332: Data Structures & Parallelism Lecture 17: Shared-Memory Concurrency & Mutual Exclusion. Ruth Anderson Winter 2019

CMSC 330: Organization of Programming Languages. OCaml Imperative Programming

Threads, Concurrency, and Parallelism

Advanced MEIC. (Lesson #18)

Consistency. CS 475, Spring 2018 Concurrent & Distributed Systems

Introduction to Concurrent Software Systems. CSCI 5828: Foundations of Software Engineering Lecture 12 09/29/2016

CS 11 Haskell track: lecture 1

Java and C CSE 351 Spring

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

Topics. File Buffer Cache for Performance. What to Cache? COS 318: Operating Systems. File Performance and Reliability

CMSC 330: Organization of Programming Languages. Memory Management and Garbage Collection

CMSC 330: Organization of Programming Languages

An Introduction to Subtyping

The Stack, Free Store, and Global Namespace

Multicore programming in Haskell. Simon Marlow Microsoft Research

Option Values, Arrays, Sequences, and Lazy Evaluation

Kotlin for Android Developers

Principles of Software Construction: Objects, Design, and Concurrency. The Perils of Concurrency Can't live with it. Cant live without it.

Reference types in Clojure. April 2, 2014

CS415 Compilers Context-Sensitive Analysis Type checking Symbol tables

Problems with Concurrency. February 19, 2014

Assigning to a Variable

CS 351 Design of Large Programs Programming Abstractions

Practicing at the Cutting Edge Learning and Unlearning about Performance. Martin Thompson

1 Shyam sir JAVA Notes

DESIGN, EVOLUTION AND USE

clojure & cfml sitting in a tree sean corfield world singles

A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 2 Analysis of Fork-Join Parallel Programs

From Java Code to Java Heap Understanding the Memory Usage of Your Application

There are many other applications like constructing the expression tree from the postorder expression. I leave you with an idea as how to do it.

CMSC 330: Organization of Programming Languages

Const Correctness. Ali Malik

ClojureScript. as a compilation target to JS. Michiel Vijay FP AMS October 16th 2014

RCU. ò Walk through two system calls in some detail. ò Open and read. ò Too much code to cover all FS system calls. ò 3 Cases for a dentry:

Transcription:

Introduction to Clojure Concurrency (and data structures) Karl Krukow, Engineer at Trifork & CTO LessPainful @karlkrukow Goto Amsterdam, May 2012

Introduction to Clojure Concurrency (and data structures) Karl Krukow, Engineer at Trifork & CTO LessPainful @karlkrukow Goto Amsterdam, May 2012

Intro to Clojure data structures (with some parallelism added too) Karl Krukow, Engineer at Trifork & CTO LessPainful @karlkrukow Goto Amsterdam, May 2012

About me PhD Computer Science, Uni. Aarhus, 2006 Engineer at Trifork for about 6 years on Web, JavaScript, Java/JEE, Ruby/Rails, ios, Conferences and Training. Clojure and Rich Hickey fan-boy! (and Keynote newbie) Recently CTO of LessPainful, automated mobile app testing: (http://www.lesspainful.com). Conj Labs - 3 day Clojure training in Europe (with Lau Jensen)

About me PhD Computer Science, Uni. Aarhus, 2006 Engineer at Trifork for about 6 years on Web, JavaScript, Java/JEE, Ruby/Rails, ios, Conferences and Training. Clojure and Rich Hickey fan-boy! (and Keynote newbie) Recently CTO of LessPainful, automated mobile app testing: (http://www.lesspainful.com). Conj Labs - 3 day Clojure training in Europe (with Lau Jensen)

Why give a talk about Data Structures?

Pop-quiz

Pop-quiz //m is a shared java.util.hashmap public static void write(final int offset) { } for (int i = 0; i < 10000; i++) { int k = offset+i; } m.put(k, -k); public static void read(final int offset) { for (int i = 0; i < 10000; i++) { } } int key = offset+i; Integer val = m.get(key); if (val!= null) { if (val.intvalue()!= -key) { } } System.out.println("Key and value don't match...");

Pop-quiz //m is a shared java.util.hashmap public static void write(final int offset) { } for (int i = 0; i < 10000; i++) { int k = offset+i; } m.put(k, -k); public static void read(final int offset) { for (int i = 0; i < 10000; i++) { } } int key = offset+i; Integer val = m.get(key); if (val!= null) { if (val.intvalue()!= -key) { } Suppose we have multiple threads calling both read and write without synchronization. Now, what can happen? } System.out.println("Key and value don't match...");

Non-obvious bug 1 ArrayIndexOutOfBoundsException? krukow:~/workspaces/trifork/concurrency$ java -cp bin hashmap.hashmapdemo Exception in thread "Thread-0" java.lang.arrayindexoutofboundsexception: 23 at java.util.hashmap.get(hashmap.java:301) at hashmap.hashmapdemo.read(hashmapdemo.java:17) at hashmap.hashmapdemo$1.run(hashmapdemo.java:32) at java.lang.thread.run(thread.java:637) WRITE done: j = 1 READ done: j = 2 READ done: j = 4 READ done: j = 6 WRITE done: j = 3 READ done: j = 8 READ done: j = 10

Non-obvious bug 2 Infinite loop! krukow:~/workspaces/trifork/concurrency$ java -cp bin hashmap.hashmapdemo READ done: j = 0 READ done: j = 2 WRITE done: j = 1 WRITE done: j = 3 READ done: j = 12 WRITE done: j = 11 READ done: j = 14 WRITE done: j = 15 WRITE done: j = 17 READ done: j = 18 READ done: j = 16 WRITE done: j = 19 ^[[A ^[[B ^[[A ^[[A ^C

Is this in theory only?

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers!

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers! Missing technical knowledge (e.g. JMM)

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers! Missing technical knowledge (e.g. JMM) Incorrect optimizations: I really can't pay the cost of synchronization (even though I haven't measured it), and in this particular case a data-race is safe.

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers! Missing technical knowledge (e.g. JMM) Incorrect optimizations: I really can't pay the cost of synchronization (even though I haven't measured it), and in this particular case a data-race is safe. Non-obvious sharing: I thought this object wasn't shared between multiple threads.

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers! Missing technical knowledge (e.g. JMM) Incorrect optimizations: I really can't pay the cost of synchronization (even though I haven't measured it), and in this particular case a data-race is safe. Non-obvious sharing: I thought this object wasn't shared between multiple threads. Design changes. In the original design this object wasn't shared. But now it is, for some reason: design change, (bad) optimizations, singleton/caching.

Is this in theory only? Of course not! I've seen the infinite loop issue put down a cluster of production servers! Missing technical knowledge (e.g. JMM) Incorrect optimizations: I really can't pay the cost of synchronization (even though I haven't measured it), and in this particular case a data-race is safe. Non-obvious sharing: I thought this object wasn't shared between multiple threads. Design changes. In the original design this object wasn't shared. But now it is, for some reason: design change, (bad) optimizations, singleton/caching. Bad library/framework. The bug may not even be in your code!

Some real-life bugs...

Some real-life bugs... Unsound optimization : MyFaces JSF Portlet Bridge Broken lazy initialization technique (+ another bug)

Some real-life bugs... Unsound optimization : MyFaces JSF Portlet Bridge Broken lazy initialization technique (+ another bug) IceFaces: bad reasoning & Design changes Store a mutable object (SwfLifecycleExecutor) in a map in application scope Each requests initializes it setting variables works in 1.8.0. broken 1.8.2

Some real-life bugs... Unsound optimization : MyFaces JSF Portlet Bridge Broken lazy initialization technique (+ another bug) IceFaces: bad reasoning & Design changes Store a mutable object (SwfLifecycleExecutor) in a map in application scope Each requests initializes it setting variables works in 1.8.0. broken 1.8.2 Spring WebFlow: unintended sharing Storing non thread-safe object in application scope https://jira.springframework.org/browse/swf-976

Some real-life bugs... Unsound optimization : MyFaces JSF Portlet Bridge Broken lazy initialization technique (+ another bug) IceFaces: bad reasoning & Design changes Store a mutable object (SwfLifecycleExecutor) in a map in application scope Each requests initializes it setting variables works in 1.8.0. broken 1.8.2 Spring WebFlow: unintended sharing... Storing non thread-safe object in application scope https://jira.springframework.org/browse/swf-976

Some real-life bugs... Unsound optimization : MyFaces JSF Portlet Bridge Broken lazy initialization technique (+ another bug) IceFaces: bad reasoning & Design changes Store a mutable object (SwfLifecycleExecutor) in a map in application scope Each requests initializes it setting variables works in 1.8.0. broken 1.8.2 Spring WebFlow: unintended sharing Storing non thread-safe object This in application bug is very scope strange, because in single user development/testing everything https://jira.springframework.org/browse/swf-976 works fine, but in production with more users, we got usually NullPointerException on bizzarre places in program like this......

Note

Note All these bugs have at their core Share mutable objects Low-level primitives: Threads, locks + JVM Memory Model

Note All these bugs have at their core Share mutable objects Low-level primitives: Threads, locks + JVM Memory Model Persistent data structures are Good! These bugs vanish when you restrict yourself to programming with immutable objects. But how to make that practical?

Note All these bugs have at their core Share mutable objects Low-level primitives: Threads, locks + JVM Memory Model Persistent data structures are Good! These bugs vanish when you restrict yourself to programming with immutable objects. But how to make that practical? It turns out, many immutable persistent data structures also are quite amenable to parallel processing.

Agenda One-slide intro to Clojure Clojure Persistent Data Structures More details: PersistentVector, PersistentHashMap A look into the future: Parallelism with reducers Summary and references

Clojure in one slide Functional dynamic language Persistent data structures, pure functions, sequence library A unique programming & concurrency model State management constructs: var, ref, atom, agent On-the-fly & AOT compilation: JVM bytecode Deep two-way JVM interop.; embraces host A LISP family member Meta programming, closures, interactivity

Remember, there is (much) more...

Remember, there is (much) more... Records, types, protocols and polymorphism Multi methods Laziness Concurrency support Parallel programming support Macros and meta programming Numeric support and type hints JVM language interop ClojureScript

Remember, there is (much) more... Records, types, protocols and polymorphism Multi methods Laziness Concurrency support Parallel programming support Macros and meta programming Numeric support and type hints JVM language interop ClojureScript Sequence library DSLs with Clojure Logic programming Meta data Persistent data structures Interactive/REPL-based programming Clojure CLR...

Clojure Philosophy

Clojure Philosophy Background: Most programs could have dramatically less state than they do - we tend to introduce state just because it is the language default (and because we are trained to do so).

Clojure Philosophy Background: Most programs could have dramatically less state than they do - we tend to introduce state just because it is the language default (and because we are trained to do so). In Clojure We rarely use mutable objects, instead immutable data structures and pure functions. Explicitly mark the (few) parts of the program that have state - reference types. State-change to reference types is managed by Clojure (no manual locking). references atomically change from referring one immutable piece of data to another.

Understanding Clojure s Persistent Data Structures

What are persistent data structures?

What are persistent data structures? Nothing to do with durability, i.e., saving to storage!

What are persistent data structures? Nothing to do with durability, i.e., saving to storage! Immutable

What are persistent data structures? Nothing to do with durability, i.e., saving to storage! Immutable There are efficient functions that take as input a PDS, and produce as output a variant of the input. The input is still available after the operation and will retain its performance characteristics.

What are persistent data structures? Nothing to do with durability, i.e., saving to storage! Immutable There are efficient functions that take as input a PDS, and produce as output a variant of the input. The input is still available after the operation and will retain its performance characteristics. For Clojure, performance characteristics usually within 1-4x of their mutable (Java) counterparts (for single ops). The input and output structures share most of their structure (which is efficient and safe).

That almost feels like magic

Data structures Slide by Rich Hickey

Understanding Persistent Vector How is a PersistentVector represented? How to do random access to the vector? How do we add elements to the vector?

? public Object nth(int i) { if(i >= 0 && i < cnt) { if(i >= tailoff()) return tail[i & 0x01f]; Object[] arr = root; for(int level = shift; level > 0; level -= 5) arr = (Object[]) arr[(i >>> level) & 0x01f]; return arr[i & 0x01f]; } throw new IndexOutOfBoundsException(); }

Implemented as wide trees

Bit partitioning Vector index es are ints (32 bit numbers) Partition bit-representation in blocks of 5. Each block corresponds to a level in the tree (note, A block is also number in range [0,31])

Bit partitioning Vector index es are ints (32 bit numbers) Partition bit-representation in blocks of 5. Each block corresponds to a level in the tree (note, A block is also number in range [0,31]) Examples 1: [00][00000][00000][00000][00000][00000][00001] 234: [00][00000][00000][00000][00000][00111][01010](10,7) 1258: [00][00000][00000][00000][00001][00111][01010](10,7,1)

Illustration 49: [...][00001][10001] (1 + 17)

Illustration 49: [...][00001][10001] (1 + 17)

Illustration 49: [...][00001][10001] (1 + 17)

Illustration 49: [...][00001][10001] (1 + 17) Level 5 Level 0

Illustration 49: [...][00001][10001] (1 + 17) Level 5 Level 0 (i >>> level) & 0x01f

Insertion / Conjoin Consider this situation again:

Insertion / Conjoin Consider this situation again:

Easy-Peacy :) public Object nth(int i) { if(i >= 0 && i < cnt) { if(i >= tailoff()) return tail[i & 0x01f]; Object[] arr = root; for(int level = shift; level > 0; level -= 5) arr = (Object[]) arr[(i >>> level) & 0x01f]; return arr[i & 0x01f]; } throw new IndexOutOfBoundsException(); } initial level (aka shift) is 10 1258: [00][00000][00000][00000][00001][00111][01010]

Adding a level

What about HashMaps? PersistentHashMap How is a PersistentHashMap represented? How to lookup the value for a key? How to add a new key-value pair? Note this presentation talks about version 1.2 of Clojure. There are changes in later versions. These are not essential to this talk.

?? (clojure 1.2 version) static int mask(int hash, int shift){ return (hash >>> shift) & 0x01f; } static int bitpos(int hash, int shift){ return 1 << mask(hash, shift); } final int index(int bit){ return Integer.bitCount(bitmap & (bit - 1)); } public LeafNode find(int hash, Object key){ int bit = bitpos(hash, shift); if((bitmap & bit)!= 0) { return nodes[index(bit)].find(hash, key); } else return null; }

Slide by Rich Hickey

Slide by Rich Hickey

BitmapIndexedNode

BitmapIndexedNode Holds an array of size < 32, pointing to children

BitmapIndexedNode Holds an array of size < 32, pointing to children Hard part is to only use as much space as is needed: If node has n children, only use size n array; and, doing a lookup on a BitmapIndexedNode to find a child must be fast constant time

BitmapIndexedNode Holds an array of size < 32, pointing to children Hard part is to only use as much space as is needed: If node has n children, only use size n array; and, doing a lookup on a BitmapIndexedNode to find a child must be fast constant time The trick is to find an efficiently computable function to map between a 5-bit number (i.e., a bit block) and index, 0 i < n in child array.

BitmapIndexedNode The bitmap

BitmapIndexedNode The bitmap Consider the mapping bitpos: [0, 31] => integer bitpos(n) = 2 n (as bitpattern: 10 n ) e.g., 13 bitpos(13) = 00000010000000000000

BitmapIndexedNode The bitmap Consider the mapping bitpos: [0, 31] => integer bitpos(n) = 2 n (as bitpattern: 10 n ) e.g., 13 bitpos(13) = 00000010000000000000 A bitmap is maintained which is a bit-pattern e.g., 00000100000001100010001000000001

BitmapIndexedNode The bitmap Consider the mapping bitpos: [0, 31] => integer bitpos(n) = 2 n (as bitpattern: 10 n ) e.g., 13 bitpos(13) = 00000010000000000000 A bitmap is maintained which is a bit-pattern e.g., 00000100000001100010001000000001 To check if an bitblock is in the array just match: 00000100000001100010001000000001 000000000000010000000000000

Bitmap: indexing

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000 Is the number of 1's below this bitpos, in the bitmap, in the above example: 4.

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000 Is the number of 1's below this bitpos, in the bitmap, in the above example: 4. 00000000000001000000000000000000

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000 Is the number of 1's below this bitpos, in the bitmap, in the above example: 4. 00000000000001000000000000000000 00000000000000111111111111111111 (index - 1)

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000 Is the number of 1's below this bitpos, in the bitmap, in the above example: 4. 00000000000001000000000000000000 00000000000000111111111111111111 (index - 1) 00000000000000100100001000000001

Bitmap: indexing For a given bitmap, e.g., 00000100000001100100001000000001 The index of an bit block, call bitpos first, e.g.: 00000000000001000000000000000000 Is the number of 1's below this bitpos, in the bitmap, in the above example: 4. 00000000000001000000000000000000 00000000000000111111111111111111 (index - 1) 00000000000000100100001000000001 On many modern processors there is an instruction CTPOP/ POPCNT (count population)

Finding a node static int mask(int hash, int shift){ return (hash >>> shift) & 0x01f; } static int bitpos(int hash, int shift){ return 1 << mask(hash, shift); } final int index(int bit){ return Integer.bitCount(bitmap & (bit - 1)); } public LeafNode find(int hash, Object key){ int bit = bitpos(hash, shift); if((bitmap & bit)!= 0) { return nodes[index(bit)].find(hash, key); } else return null; }

Slide by Structural Sharing Rich Hickey Past Next

Other benefits

Other benefits PersistentHashMap and PersistentVector amenable to parallel processing. Divide and conquer (you already did half the work :) No special parallel collection types needed

Other benefits PersistentHashMap and PersistentVector amenable to parallel processing. Divide and conquer (you already did half the work :) No special parallel collection types needed Clojure 1.5 will (likely) have a parallel processing function fold which takes a combining fn, a reducing fn and a collection Uses Java s Fork/Join framework for parallel processing. Code has the same shape as existing (serial) clojure code

Other benefits PersistentHashMap and PersistentVector amenable to parallel processing. Divide and conquer (you already did half the work :) No special parallel collection types needed Clojure 1.5 will (likely) have a parallel processing function fold which takes a combining fn, a reducing fn and a collection Uses Java s Fork/Join framework for parallel processing. Code has the same shape as existing (serial) clojure code Live Example?

Summary For your own sake, please start doing functional programming. Benefits Sanity Concurrency Parallelism (and it s fun too) I recommend trying out Clojure :) Option clj-ds: https://github.com/krukow/clj-ds Port of Clojure s persistent data structures to use (when stuck) with other JVM languages To be updated to support the upcoming parallel processing in Clojure

References Rich Hickey Are we there yet? http:// www.infoq.com/presentations/ Are-We-There-Yet-Rich-Hickey Clojure concurrency http:// blip.tv/clojure/clojureconcurrency-819147 http://clojure.com/blog/ 2012/05/08/reducers-a-libraryand-model-for-collectionprocessing.html http://clojure.com/blog/ 2012/05/15/anatomy-ofreducer.html My Blog http://blog.higher-order.net/ 2009/02/01/understandingclojures-persistentvectorimplementation http://blog.higher-order.net/ 2009/09/08/understandingclojures-persistenthashmapdeftwice/

References Chas Emerick, Brian Carper, & Christophe Grand: Clojure Programming, 2012, O Reilly clj-ds: https://github.com/krukow/clj-ds Port of Clojure s persistent data structures to use (when stuck) with other JVM languages Conj Labs - 3 day Clojure training in Europe Lau Jensen & Karl Krukow Contact me karl@lesspainful.com for details To be updated to support parallelism (via Clojure s reducers)

Questions? Making app testing less painful... Please contact us with any questions: contact@lesspainful.com karl@lesspainful.com - ios jonas@lesspainful.com - Android http://www.lesspainful.com