Recent Posts
Archives

Posts Tagged ‘Immutability’

PostHeaderIcon [reClojure2025] UI, Pure and Simple

Lecturer

Christian Johansen is a highly experienced software developer at the Norwegian Food Safety Authority, where he specializes in architecting robust systems using Clojure and Datomic. With a professional career spanning two decades, Christian has dedicated the last ten years to full-time Clojure development. His expertise is deeply rooted in web technologies, encompassing the entire delivery pipeline from infrastructure configuration and data modeling to sophisticated frontend implementation. Beyond his primary role, he is a prolific contributor to the Clojure ecosystem, maintaining various open-source libraries and frequently sharing insights through conference presentations, educational courses, and a collaborative professional blog.

Abstract

Modern frontend development is often plagued by the complexities of shared mutable state and pervasive side effects, which complicate testing and maintenance. This article examines the philosophical and technical foundations of achieving a truly functional user interface. By revisiting the original promise of React—that UI is a function of state—and refining it through the lens of Clojure’s immutability, we introduce a paradigm known as top-down rendering. Central to this discussion is Replicant, a small, dependency-free Clojure rendering library designed to treat UI as pure, deterministic data. We analyze the methodology of building modular UIs that decouple rendering from state management, utilize data-driven event handlers, and leverage declarative animations to create simpler, more testable applications.

Historical Context and the Pursuit of Purity

The evolution of modern web development reached a significant milestone in 2013 with the introduction of React. The framework proposed a revolutionary conceptual model: the user interface should be viewed as a pure function of application state. In this ideal scenario, developers would write code as if the entire UI were rendered from scratch with every update, leaving the heavy lifting of DOM manipulation to the framework. However, while React transformed the industry’s mental model, it did not fully deliver on the promise of functional purity. In practice, React applications often allow mutable state to proliferate throughout the component tree, leading to the very “side-effect-ridden” complexity it sought to solve.
The ClojureScript community recognized this gap early on. Developers sought a more rigorous adherence to functional principles. One notable advancement was the library Quiescent, which introduced the constraint of “top-down rendering.” In this model, components are prohibited from maintaining their own internal state or triggering their own re-renders. Instead, the entire UI is a literal projection of a central, immutable data structure. This approach aligns perfectly with Clojure’s core strengths, providing a foundation for UIs that are stateless, deterministic, and built entirely on data.

Methodology: Rendering with Replicant

Replicant serves as a realization of this top-down philosophy. It is a minimalist virtual DOM library that operates on Hiccup, a domain-specific language in Clojure that represents HTML structures as standard data vectors and maps. The core methodology involves creating pure functions that transform domain data into Hiccup data. Because these functions are pure, they are inherently predictable and easy to test in isolation.
To illustrate this, consider the rendering of a task card in a Kanban application. The developer defines a function that takes a task map and returns a Hiccup representation. Replicant’s render function then takes this data and a target DOM element to perform the initial mount. When the application state changes, the process is repeated: the pure function generates new Hiccup data, and Replicant calculates the minimal set of DOM mutations required to transition the view. This “diffing” process ensures efficiency without requiring the developer to manage state transitions manually.

Code Sample: Pure Hiccup Transformation

(defn render-task [task tags-lookup]
  [:div.task-card
   [:h3 (:task/title task)]
   [:div.tags
    (map (fn [tag-id]
           (let [tag (get tags-lookup tag-id)]
             [:span {:class (str "tag-" (:tag/color tag))}
              (:tag/label tag)]))
         (:task/tags task))]])

Advanced UI Patterns: Events and Animations

Beyond static rendering, the “pure and simple” approach extends to interactivity. In traditional frameworks, event handlers are often opaque functions that execute side effects directly. Replicant encourages data-driven event handlers. Instead of passing a callback function to an onClick attribute, the developer can pass a data structure—a vector or a map—representing the intent of the event. A central coordinator then interprets this data to update the global state. This decoupling makes the UI’s behavior as inspectable and testable as its appearance.
The same principle applies to complex UI requirements like animations and timed effects. By treating time and transitions as part of the data flow, developers can create declarative animations. These are not imperative commands to “fade in an element” but rather state-based descriptions of how an element should appear at a given point in the application lifecycle. This approach dramatically simplifies the creation of interactive features like drag-and-drop or live data streaming, as the UI remains a consistent reflection of the underlying store regardless of where the data originates.

Consequential Benefits and Conclusion

Adopting a stateless, data-centric approach to UI development yields significant benefits for software quality. Because the UI is composed of pure functions, it is highly modular and testable. Tools like Portfolio (similar to Storybook for ClojureScript) allow developers to render “scenes” in isolation by passing mock domain data to their rendering functions. This facilitates rapid prototyping and visual regression testing without the need to navigate through a live, stateful application.
Ultimately, the shift toward pure and simple UIs represents a move away from the “nashing of teeth” associated with shared mutable state. By leveraging Clojure’s immutable data structures and Replicant’s minimalist rendering engine, developers can build systems that are not only more robust and maintainable but also more enjoyable to create. The decoupling of rendering from state management allows for a degree of architectural clarity that is often missing in contemporary frontend development.

Links:

PostHeaderIcon [DevoxxBE2025] Finally, Final Means Final: A Deep Dive into Field Immutability in Java

Lecturer

Per Minborg is a Java Core Library Developer at Oracle, specializing in language features and performance improvements. He has contributed to projects enhancing Java’s concurrency and immutability models, drawing from his background in database acceleration tools like Speedment.

Abstract

This investigation explores Java’s ‘final’ keyword as a mechanism for immutability, examining its semantic guarantees from compilation to runtime execution. It contextualizes the balance between mutable flexibility and immutable predictability, delving into JVM trust dynamics and recent proposals like strict finals. Through code demonstrations and JVM internals, the narrative assesses approaches for stronger immutability, including lazy constants, and their effects on threading, optimization, and application speed. Forward implications for Java’s roadmap are considered, emphasizing enhanced reliability and efficiency.

Balancing Mutability and Immutability in Java

Java’s design philosophy accommodates both mutability for dynamic state changes and immutability for stability, yet tensions arise in performance and safety. Immutability, praised in resources like Effective Java, ensures objects maintain a single state, enabling safe concurrent access without locks. Immutable instances can be cached or reused, functioning reliably as keys in collections—unlike mutables that may cause hash inconsistencies after alterations.

Mutability, however, provides adaptability for evolving data, crucial in interactive systems. The ‘final’ keyword aims to enforce constancy, but runtime behaviors complicate this. Declaring ‘final int value = 42;’ implies permanence, yet advanced techniques like reflection with Unsafe can modify it, breaching expectations.

This duality impacts optimizations: trusted finals allow constant propagation, minimizing memory accesses. Yet, Java permits post-constructor updates for deserialization, eroding trust. Demonstrations show reflection altering finals, highlighting vulnerabilities where JVMs must hedge against changes, forgoing inlining.

Historically, Java prioritized flexibility, allowing such mutations for practicality, but contemporary demands favor integrity. Initiatives like “final truly final” seek to ban post-construction changes, bolstering trust for aggressive enhancements.

JVM Trust Mechanisms and Final Field Handling

JVM trust refers to assuming ‘final’ fields remain unchanged post-initialization, enabling efficiencies like constant folding. Current semantics, however, permit reflective or deserialization modifications, limiting optimizations.

Examples illustrate:

class Coordinate {
    final int coord;
    Coordinate(int coord) { this.coord = coord; }
}

Post-constructor, ‘coord’ is trusted, supporting optimizations. Reflection via Field.setAccessible(true) overrides this, as Unsafe manipulations demonstrate. Performance tests reveal trusted finals outperform volatiles in accesses, but untrusted ones underperform.

Java’s model historically allowed mutations for deserialization, but stricter enforcement proposals aim to restrict this. Implications include safer multi-threading, as finals ensure visibility without volatiles.

Advancements in Strict Finals and Leak Prevention

Strict finals strengthen guarantees by prohibiting constructor leaks—publishing ‘this’ before all finals are set. This prevents partial states in concurrent environments, where threads might observe defaults like zero before assignments.

Problematic code:

class LeakyClass {
    final int val = 42;
    LeakyClass() { Registry.register(this); } // Leak
}

Leaks risk threads seeing val=0. Strict finals reject this at compile time, enforcing safe initialization.

Methodologically, this requires refactoring to avoid early publications, but yields threading reliability and optimization opportunities. Benchmarks quantify benefits: trusted paths execute quicker, with fewer barriers.

Lazy Constants for Deferred Initialization

Previewed in Java 25, lazy constants merge mutable deferral with final optimizations. Declared ‘lazy final int val;’, they compute once on access via suppliers:

lazy final int heavy = () -> heavyComputation();

JVM views them as constants after initialization, supporting folding. Use cases include infrequent or expensive values, avoiding startup costs.

Unlike volatiles, lazy constants ensure at-most-once execution, with threads blocking on contention. This fits singletons or caches, surpassing synchronized options in efficiency.

Roadmap and Performance Consequences

Strict finals and lazy constants fortify Java’s immutability, complementing concurrent trends. Consequences include accelerated, secure code, with JVMs exploiting trust for vector operations.

Developers balance: finals for absolutes, lazies for postponements. Roadmaps indicate stabilization in Java 26, expanding usage.

In overview, these evolutions make ‘final’ definitively final, boosting Java’s sturdiness and proficiency.

Links:

  • Lecture video: https://www.youtube.com/watch?v=J754RsoUd00
  • Per Minborg on Twitter/X: https://twitter.com/PMinborg
  • Oracle website: https://www.oracle.com/

PostHeaderIcon [DevoxxPL2022] Are Immortal Libraries Ready for Immutable Classes? • Tomasz Skowroński

At Devoxx Poland 2022, Tomasz Skowroński, a seasoned Java developer, delivered a compelling presentation exploring the readiness of Java libraries for immutable classes. With a focus on the evolving landscape of Java programming, Tomasz dissected the challenges and opportunities of adopting immutability in modern software development. His talk provided a nuanced perspective on balancing simplicity, clarity, and robustness in code design, offering practical insights for developers navigating the complexities of mutable and immutable paradigms.

The Allure and Pitfalls of Mutable Classes

Tomasz opened his discourse by highlighting the appeal of mutable classes, likening them to a “shy green boy” for their ease of use and rapid development. Mutable classes, with their familiar getters and setters, simplify coding and accelerate project timelines, making them a go-to choice for many developers. However, Tomasz cautioned that this simplicity comes at a cost. As fields and methods accumulate, mutable classes grow increasingly complex, undermining their initial clarity. The internal state becomes akin to a data structure, vulnerable to unintended modifications, which complicates maintenance and debugging. This fragility, he argued, often leads to issues like null pointer exceptions and challenges in maintaining a consistent state, particularly in large-scale systems.

The Promise of Immutability

Transitioning to immutability, Tomasz emphasized its role in fostering robust and predictable code. Immutable classes, by preventing state changes after creation, offer a safeguard against unintended modifications, making them particularly valuable in concurrent environments. He clarified that immutability extends beyond merely marking fields as final or using tools like Lombok. Instead, it requires a disciplined approach to design, ensuring objects remain unalterable. Tomasz highlighted Java records and constructor-based classes as practical tools for achieving immutability, noting their ability to streamline code while maintaining clarity. However, he acknowledged that immutability introduces complexity, requiring developers to rethink traditional approaches to state management.

Navigating Java Libraries with Immutability

A core focus of Tomasz’s presentation was the compatibility of Java libraries with immutable classes. He explored tools like Jackson for JSON deserialization, noting that while modern libraries support immutability through annotations like @ConstructorProperties, challenges persist. For instance, deserializing complex objects may require manual configuration or reliance on Lombok to reduce boilerplate. Tomasz also discussed Hibernate, where immutable entities, such as events or finalized invoices, can express domain constraints effectively. By using the @Immutable annotation and configuring Hibernate to throw exceptions on modification attempts, developers can enforce immutability, though direct database operations remain a potential loophole.

Practical Strategies for Immutable Design

Tomasz offered actionable strategies for integrating immutability into everyday development. He advocated for constructor-based dependency injection over field-based approaches, reducing boilerplate with tools like Lombok or Java records. For RESTful APIs, he suggested mapping query parameters to immutable DTOs, enhancing clarity and reusability. In the context of state management, Tomasz proposed modeling state transitions in immutable classes using interfaces and type-safe implementations, as illustrated by a rocket lifecycle example. This approach ensures predictable state changes without the risks associated with mutable methods. Additionally, he addressed performance concerns, arguing that the overhead of object creation in immutable designs is often overstated, particularly in web-based systems where network latency dominates.

Testing and Tooling Considerations

Testing immutable classes presents unique challenges, particularly with tools like Mockito. Tomasz noted that while Mockito supports final classes in newer versions, mocking immutable objects may indicate design flaws. Instead, he recommended creating real objects via constructors for testing, emphasizing their intentional design for construction. For developers working with legacy systems or external libraries, Tomasz advised cautious adoption of immutability, leveraging tools like Terraform for infrastructure consistency and Java’s evolving ecosystem to reduce boilerplate. His pragmatic approach underscored the importance of aligning immutability with project goals, avoiding dogmatic adherence to either mutable or immutable paradigms.

Embracing Immutability in Java’s Evolution

Concluding his talk, Tomasz positioned immutability as a cornerstone of Java’s ongoing evolution, from records to potential future enhancements like immutable collections. He urged developers to reduce mutation in their codebases and consider immutability beyond concurrency, citing benefits in caching, hashing, and overall design clarity. While acknowledging that mutable classes remain suitable for certain use cases, such as JPA entities in dynamic domains, Tomasz advocated for a mindful approach to code design, prioritizing immutability where it enhances robustness and maintainability.

Links:

PostHeaderIcon [DevoxxBE2012] First Steps with Scala (Part 1/2)

In an engaging session, Dick Wall and Bill Venners, co-founders of Escalate Software and prominent figures in the Scala community, introduced newcomers to the Scala programming language. Dick, known for his JavaPosse involvement, and Bill, president of Artima and author of key Java texts, adapted their training curriculum to cover foundational elements. Their approach fused object-oriented and functional paradigms, emphasizing Scala’s blend of familiarity and innovation.

They commenced with the Scala REPL, a tool they use daily for experimentation. This interactive shell allows immediate code execution, inferring types and storing results for reuse, making it invaluable even for Java developers exploring libraries.

Dick and Holly—wait, Dick and Bill—highlighted Scala’s strong typing with inference, reducing boilerplate while maintaining safety. They demonstrated basic operations, showing how everything integrates into a unified hierarchy, unlike Java’s primitives and references.

Defining Variables and Immutability

Transitioning to variables, Dick and Bill distinguished between vals (immutable) and vars (mutable), promoting vals for reliability. This design choice encourages constant use without extra syntax, contrasting Java’s final keyword. They illustrated reassignment errors, noting REPL’s scoping allows redefinitions, mimicking nested blocks.

Type specifications, optional due to inference, follow names with colons, inverting Java’s order for natural flow. Examples showed string inferences and explicit declarations, underscoring flexibility.

They addressed mutability choices, advising private volatile vars for necessary changes, or mutable objects within immutable structures, depending on context.

Control Structures and Expressions

Dick and Bill explored control structures, revealing Scala’s expression-oriented nature. If statements return values, enabling concise assignments without ternaries. While loops, though imperative, yield Unit, encouraging functional alternatives.

For comprehensions, powerful for iteration, support guards and yields, transforming collections declaratively. They demonstrated filtering even numbers or yielding transformed lists, highlighting pattern matching integration.

Try-catch blocks, also expressions, handle exceptions functionally, with finally clauses for cleanup. This uniformity simplifies code, treating controls as value producers.

Functions and Closures

Delving into functions, they defined them as objects, enabling higher-order usage. Simple defs showed parameter typing and inference, with return types often omitted.

Function literals, akin to lambdas, capture environments as closures, allowing deferred execution. Examples illustrated anonymous functions for mapping or filtering, emphasizing Scala’s functional leanings.

They introduced by-name parameters for lazy evaluation, useful in custom controls like loops, mimicking built-in syntax without special privileges.

Collections and Functional Programming

Scala collections, immutable by default, support transformations via methods like map and filter. Dick and Bill showcased creating lists, accessing elements, and applying operations, yielding new collections without mutation.

They contrasted mutable variants, advising caution for concurrency. For comprehensions over collections generate new ones, combining iteration with functional purity.

Approaching functional style, they encouraged avoiding side effects, using recursion or folds for aggregations, fostering predictable code.

Advanced Basics and Q&A Insights

In Q&A, they addressed optimizations: Scala uses primitives internally, with @specialized for generics avoiding boxing. Profiling mirrors Java tools, with quirks in coverage.

Dick and Bill wrapped by previewing afternoon labs on Scala Koans, urging practice. Their session laid a solid groundwork, blending theory with practical demos, preparing attendees for Scala’s expressive power.

Links: