Clojure: High Performance JVM Programming

Book description

Explore the world of lightning fast Clojure apps with asynchronous channels, logic, reactive programming, and more

About This Book

  • Discover Clojure's features and advantages and use them in your existing projects
  • Explore lesser-known and more advanced features, constructs, and methodologies such as asynchronous channels, actors, logic programming, and reactive programming
  • Measure and monitor performance, and understand optimization techniques

Who This Book Is For

If you're looking to learn more about its core libraries and delve into the Clojure language in detail, then this book is ideal for you. Prior knowledge of the Clojure language is required.

What You Will Learn

  • Understand tools for the Clojure world and how they relate to Java tools and standards (such as Maven)
  • Write simple multicore programs using Clojure's core concepts, such as atoms, agents, and refs
  • Get to grips with Clojure's concurrency and state-management primitives in depth
  • Analyze latency using the Criterium library
  • Avoid reflection and boxing with type hints
  • Maximize the impact of parallelization, functional composition, and process transformation by composing reducers and transducers
  • Modify and add features to the Clojure language using macros
  • Test your code with unit tests, specs, and type checks to write testable code
  • Troubleshoot and style your Clojure code to make it more maintainable

In Detail

Clojure is a general-purpose language from the Lisp family with an emphasis on functional programming. It has some interesting concepts and features such as immutability, gradual typing, thread-safe concurrency primitives, and macro-based metaprogramming, which makes it a great choice to create modern, performant, and scalable applications.

This learning path aims at unleashing the true potential of the Clojure language so you can use it in your projects. It begins with installing and setting up the Clojure environment before moving on to explore the language in depth. You'll get acquainted with its various features such as functional programming, concurrency, reducers, transducers, core.async and core.logic, and so on with a great level of detail.

Moving on, you'll also learn how to enhance performance using Java interoperability and JVM-specific features from Clojure; you'll even master language features such as asynchronous channels, actors, logic programming, reactive programming, metaprogramming, and so on.

This learning path combines some of the best that Packt has to offer in one complete, curated package. It includes content from the following Packt products:

  • Clojure for Java Developers by Eduardo Díaz
  • Clojure High Performance Programming, Second Edition by Shantanu Kumar
  • Mastering Clojure by Akhil Wali

Style and approach

This is an easy-to-follow, step-by-step guide to start writing Clojure programs, making use of all of its varied features and advantages.

Table of contents

  1. Clojure: High Performance JVM Programming
    1. Table of Contents
    2. Clojure: High Performance JVM Programming
    3. Credits
    4. Preface
      1. What this learning path covers
      2. What you need for this learning path
      3. Who this learning path is for
      4. Reader feedback
      5. Customer support
        1. Downloading the example code
        2. Errata
        3. Piracy
        4. Questions
    5. 1. Module 1
      1. 1. Getting Started with Clojure
        1. Getting to know Clojure
        2. Installing Leiningen
        3. Using a REPL
          1. The nREPL protocol
          2. Hello world
          3. REPL utilities and conventions
        4. Creating a new project
          1. Project structure
          2. Creating a standalone app
        5. Using Cursive Clojure
          1. Installing Cursive Clojure
        6. Getting started with Clojure code and data
          1. Lists in Clojure
          2. Operations in Clojure
          3. Functions in Clojure
        7. Clojure's data types
          1. Scalars
          2. Collection data types
        8. Summary
      2. 2. Namespaces, Packages, and Tests
        1. Namespaces in Clojure
          1. Packages in Clojure
        2. The classpath and the classloader
          1. Back to Clojure namespaces
            1. Playing with namespaces
          2. Creating a new namespace
          3. Working with namespaces on the REPL
        3. Testing in Clojure
          1. Testing from the command line
          2. Testing in IntelliJ
        4. Summary
      3. 3. Interacting with Java
        1. Using Maven dependencies
        2. Clojure interop syntax
          1. Creating an object
          2. Calling an instance method
          3. Calling a static method or function
          4. Accessing inner classes
        3. Writing a simple image namespace
        4. Writing the tests
          1. The let statement
        5. Destructuring in Clojure
          1. Sequential destructuring
          2. Associative destructuring
        6. Exposing your code to Java
          1. Testing from Groovy
        7. Proxy and reify
        8. Summary
      4. 4. Collections and Functional Programming
        1. Basics of functional programming
        2. Persistent collections
        3. Types of collections in Clojure
        4. The sequence abstraction
        5. Specific collection types in Clojure
          1. Vectors
          2. Lists
          3. Maps
            1. Sorted maps and hash maps
            2. Common properties
          4. Sets
            1. Sorted sets and hash sets
            2. Common properties
            3. Union, difference, and intersection
          5. Applying functional programming to collections
            1. The imperative programming model
            2. The functional paradigm
            3. Functional programming and immutability
            4. Laziness
        6. Summary
      5. 5. Multimethods and Protocols
        1. Polymorphism in Java
        2. Multimethods in Clojure
          1. Keyword hierarchies
            1. isa?
            2. parents
            3. descendants
            4. underive
          2. A la carte dispatch functions
        3. Protocols in Clojure
          1. Records in Clojure
        4. Summary
      6. 6. Concurrency
        1. Using your Java knowledge
        2. The Clojure model of state and identity
        3. Promises
          1. Pulsar and lightweight threads
        4. Futures
        5. Software transactional memory and refs
        6. Atoms
        7. Agents
          1. Validators
          2. Watchers
        8. core.async
          1. Why lightweight threads?
          2. Goblocks
          3. Channels
          4. Transducers
        9. Summary
      7. 7. Macros in Clojure
        1. Lisp's foundational ideas
        2. Macros as code modification tools
          1. Modifying code in Java
          2. Modifying code in Groovy
            1. The @ToString annotation
            2. The @TupleConstructor annotation
            3. The @Slf4j annotation
        3. Writing your first macro
        4. Debugging your first macro
          1. Quote, syntax quote, and unquoting
          2. Unquote splicing
          3. gensym
        5. Macros in the real world
        6. References
        7. Summary
    6. 2. Module 2
      1. 1. Performance by Design
        1. Use case classification
          1. The user-facing software
          2. Computational and data-processing tasks
            1. A CPU bound computation
            2. A memory bound task
            3. A cache bound task
            4. An input/output bound task
          3. Online transaction processing
          4. Online analytical processing
          5. Batch processing
        2. A structured approach to the performance
        3. The performance vocabulary
          1. Latency
          2. Throughput
          3. Bandwidth
          4. Baseline and benchmark
          5. Profiling
          6. Performance optimization
          7. Concurrency and parallelism
          8. Resource utilization
          9. Workload
        4. The latency numbers that every programmer should know
        5. Summary
      2. 2. Clojure Abstractions
        1. Non-numeric scalars and interning
        2. Identity, value, and epochal time model
          1. Variables and mutation
          2. Collection types
        3. Persistent data structures
          1. Constructing lesser-used data structures
          2. Complexity guarantee
            1. O(<7) implies near constant time
          3. The concatenation of persistent data structures
        4. Sequences and laziness
          1. Laziness
            1. Laziness in data structure operations
            2. Constructing lazy sequences
              1. Custom chunking
              2. Macros and closures
        5. Transducers
          1. Performance characteristics
        6. Transients
          1. Fast repetition
        7. Performance miscellanea
          1. Disabling assertions in production
          2. Destructuring
          3. Recursion and tail-call optimization (TCO)
            1. Premature end of iteration
          4. Multimethods versus protocols
          5. Inlining
        8. Summary
      3. 3. Leaning on Java
        1. Inspecting the equivalent Java source for Clojure code
          1. Creating a new project
          2. Compiling the Clojure sources into Java bytecode
          3. Decompiling the .class files into Java source
          4. Compiling the Clojure source without locals clearing
        2. Numerics, boxing, and primitives
        3. Arrays
        4. Reflection and type hints
          1. An array of primitives
          2. Primitives
          3. Macros and metadata
            1. String concatenation
          4. Miscellaneous
        5. Using array/numeric libraries for efficiency
          1. HipHip
          2. primitive-math
            1. Detecting boxed math
        6. Resorting to Java and native code
          1. Proteus – mutable locals in Clojure
        7. Summary
      4. 4. Host Performance
        1. The hardware
          1. Processors
            1. Branch prediction
            2. Instruction scheduling
            3. Threads and cores
          2. Memory systems
            1. Cache
            2. Interconnect
          3. Storage and networking
        2. The Java Virtual Machine
          1. The just-in-time compiler
          2. Memory organization
          3. HotSpot heap and garbage collection
          4. Measuring memory (heap/stack) usage
            1. Determining program workload type
          5. Tackling memory inefficiency
        3. Measuring latency with Criterium
          1. Criterium and Leiningen
        4. Summary
      5. 5. Concurrency
        1. Low-level concurrency
          1. Hardware memory barrier (fence) instructions
          2. Java support and the Clojure equivalent
        2. Atomic updates and state
          1. Atomic updates in Java
          2. Clojure's support for atomic updates
            1. Faster writes with atom striping
        3. Asynchronous agents and state
          1. Asynchrony, queueing, and error handling
          2. Why you should use agents
          3. Nesting
        4. Coordinated transactional ref and state
          1. Ref characteristics
          2. Ref history and in-transaction deref operations
          3. Transaction retries and barging
          4. Upping transaction consistency with ensure
          5. Lesser transaction retries with commutative operations
          6. Agents can participate in transactions
          7. Nested transactions
          8. Performance considerations
        5. Dynamic var binding and state
        6. Validating and watching the reference types
        7. Java concurrent data structures
          1. Concurrent maps
          2. Concurrent queues
          3. Clojure support for concurrent queues
        8. Concurrency with threads
          1. JVM support for threads
          2. Thread pools in the JVM
          3. Clojure concurrency support
            1. Future
            2. Promise
        9. Clojure parallelization and the JVM
          1. Moore's law
          2. Amdahl's law
          3. Universal Scalability Law
          4. Clojure support for parallelization
            1. pmap
            2. pcalls
            3. pvalues
          5. Java 7's fork/join framework
        10. Parallelism with reducers
          1. Reducible, reducer function, reduction transformation
          2. Realizing reducible collections
          3. Foldable collections and parallelism
        11. Summary
      6. 6. Measuring Performance
        1. Performance measurement and statistics
          1. A tiny statistics terminology primer
            1. Median, first quartile, third quartile
            2. Percentile
            3. Variance and standard deviation
          2. Understanding Criterium output
          3. Guided performance objectives
        2. Performance testing
          1. The test environment
          2. What to test
          3. Measuring latency
            1. Comparative latency measurement
            2. Latency measurement under concurrency
          4. Measuring throughput
            1. Average throughput test
          5. The load, stress, and endurance tests
        3. Performance monitoring
          1. Monitoring through logs
          2. Ring (web) monitoring
          3. Introspection
            1. JVM instrumentation via JMX
        4. Profiling
          1. OS and CPU/cache-level profiling
          2. I/O profiling
        5. Summary
      7. 7. Performance Optimization
        1. Project setup
          1. Software versions
          2. Leiningen project.clj configuration
            1. Enable reflection warning
            2. Enable optimized JVM options when benchmarking
          3. Distinguish between initialization and runtime
        2. Identifying performance bottlenecks
          1. Latency bottlenecks in Clojure code
            1. Measure only when it is hot
          2. Garbage collection bottlenecks
            1. Threads waiting at GC safepoint
            2. Using jstat to probe GC details
          3. Inspecting generated bytecode for Clojure source
          4. Throughput bottlenecks
        3. Profiling code with VisualVM
        4. The Monitor tab
          1. The Threads tab
          2. The Sampler tab
            1. Setting the thread name
          3. The Profiler tab
          4. The Visual GC tab
          5. The Alternate profilers
        5. Performance tuning
          1. Tuning Clojure code
            1. CPU/cache bound
            2. Memory bound
            3. Multi-threaded
          2. I/O bound
          3. JVM tuning
          4. Back pressure
        6. Summary
      8. 8. Application Performance
        1. Choosing libraries
          1. Making a choice via benchmarks
            1. Web servers
            2. Web routing libraries
            3. Data serialization
            4. JSON serialization
            5. JDBC
        2. Logging
          1. Why SLF4J/LogBack?
          2. The setup
            1. Dependencies
            2. The logback configuration file
            3. Optimization
        3. Data sizing
          1. Reduced serialization
          2. Chunking to reduce memory pressure
            1. Sizing for file/network operations
            2. Sizing for JDBC query results
        4. Resource pooling
          1. JDBC resource pooling
        5. I/O batching and throttling
          1. JDBC batch operations
          2. Batch support at API level
          3. Throttling requests to services
        6. Precomputing and caching
        7. Concurrent pipelines
          1. Distributed pipelines
        8. Applying back pressure
          1. Thread pool queues
          2. Servlet containers such as Tomcat and Jetty
          3. HTTP Kit
          4. Aleph
        9. Performance and queueing theory
          1. Little's law
            1. Performance tuning with respect to Little's law
        10. Summary
    7. 3. Module 3
      1. 1. Working with Sequences and Patterns
        1. Defining recursive functions
        2. Thinking in sequences
          1. Using the seq library
            1. Creating sequences
            2. Transforming sequences
            3. Filtering sequences
            4. Lazy sequences
          2. Using zippers
        3. Working with pattern matching
        4. Summary
      2. 2. Orchestrating Concurrency and Parallelism
        1. Managing concurrent tasks
          1. Using delays
          2. Using futures and promises
        2. Managing state
          1. Using vars
          2. Using refs
          3. Using atoms
          4. Using agents
        3. Executing tasks in parallel
          1. Controlling parallelism with thread pools
        4. Summary
      3. 3. Parallelization Using Reducers
        1. Using reduce to transform collections
          1. What's wrong with sequences?
          2. Introducing reducers
        2. Using fold to parallelize collections
        3. Processing data with reducers
        4. Summary
      4. 4. Metaprogramming with Macros
        1. Understanding the reader
        2. Reading and evaluating code
        3. Quoting and unquoting code
        4. Transforming code
          1. Expanding macros
          2. Creating macros
          3. Encapsulating patterns in macros
          4. Using reader conditionals
          5. Avoiding macros
        5. Summary
      5. 5. Composing Transducers
        1. Understanding transducers
          1. Producing results from transducers
        2. Comparing transducers and reducers
        3. Transducers in action
          1. Managing volatile references
          2. Creating transducers
        4. Summary
      6. 6. Exploring Category Theory
        1. Demystifying category theory
        2. Using monoids
        3. Using functors
        4. Using applicative functors
        5. Using monads
        6. Summary
      7. 7. Programming with Logic
        1. Diving into logic programming
          1. Solving logical relations
          2. Combining logical relations
        2. Thinking in logical relations
          1. Solving the n-queens problem
          2. Solving a Sudoku puzzle
        3. Summary
      8. 8. Leveraging Asynchronous Tasks
        1. Using channels
          1. Customizing channels
          2. Connecting channels
          3. Revisiting the dining philosophers problem
        2. Using actors
          1. Creating actors
          2. Passing messages between actors
          3. Handling errors with actors
          4. Managing state with actors
          5. Comparing processes and actors
        3. Summary
      9. 9. Reactive Programming
        1. Reactive programming with fibers and dataflow variables
        2. Using Reactive Extensions
        3. Using functional reactive programming
        4. Building reactive user interfaces
        5. Introducing Om
        6. Summary
      10. 10. Testing Your Code
        1. Writing tests
          1. Defining unit tests
          2. Using top-down testing
        2. Testing with specs
        3. Generative testing
        4. Testing with types
        5. Summary
      11. 11. Troubleshooting and Best Practices
        1. Debugging your code
          1. Using tracing
          2. Using Spyscope
        2. Logging errors in your application
        3. Thinking in Clojure
        4. Summary
      12. A. References
    8. Bibliography
    9. Index

Product information

  • Title: Clojure: High Performance JVM Programming
  • Author(s): Eduardo Díaz, Shantanu Kumar, Akhil Wali
  • Release date: January 2017
  • Publisher(s): Packt Publishing
  • ISBN: 9781787129597