Functional Bytes Clojure, Scala en Java specialist

Redelay 2.0 released

Mar 3, 2024 • Arnout Roemers

The redelay library - the unassuming state lifecycle tool - had several unreleased improvements piling up. Some of these are breaking, so a new major version has finally been released. Upgrading should be easy though. This short post goes through the improvements.

What is redelay again?

The redelay library is for starting and stopping the stateful parts of your Clojure application, in the correct order. There are more libraries like this - like integrant, component, or mount. Redelay brings this down its core in a flexible way, by having State objects that can live anywhere. Think of them like Clojure’s delay, but stoppable.

To give a small example, this is what it looks like:

(def two (state 2))
(def sum (state (+ 40 @two) :stop (println "Stopping" this)))

;=> #<State@329663[user/state--313]: :unrealized>

;=> 42

;=> (#<State@247136[user/state--312]: 2>
;=>  #<State@329663[user/state--313]: 42>)

;> Stopping 42
;=> (#<State@329663[user/state--313]: :unrealized>
;=>  #<State@247136[user/state--312]: :unrealized>)

Just plain objects, no framework to follow. All of the above has not changed with the new major release. The devil is in the smaller details.

Have a peek

The State object now supports Clojure’s peek function. This can be used to inspect the value of a State, but it won’t start it if is not realized already.

(peek sum)
;=> nil

;=> 42

(peek sum)
;=> 42

This makes the State object more complete and useable with Clojure’s fantastic core toolbelt.

Dynamic defstate

The defstate macro - which is similar to (def .. (state ..)) - is now automatically tagged as :dynamic. This allows for easy rebinding the state in tests. For example:

(defstate config (load-config))
(defstate db (start-db @config))

(binding [config (delay {:static "config"})]
  (with-open [test-db db] test here...)

So you can remove the ^:dynamic tags from your code if you were using the defstate like this already. Or, perhaps it inspires you for structuring your tests like this.

Watch it (breaking change)

The library has an extension point, called the watchpoint. You can add callbacks to this, using Clojure’s add-watch. This is still the case in version 2.0, however the arguments to the callback are now different.

As you might know, a watch callback receives four arguments: a key, the reference, its old-state, and its new-state. In version 1.0 a stopped State would be put in the third (old-state) argument, and a started State would be put in the fourth (new-state) argument.

In version 2.0 the arguments to the callback are different, and the callbacks happen more often. The third argument is now one of :starting, :started, :stopping and :stopped, and the fourth argument is the State object it concerns. For example:

(add-watch redelay.core/watchpoint :my-logger prn)

;> :my-logger #'redelay.core/watchpoint :starting #<State@5a42f4[user/config]: :unrealized>
;> :my-logger #'redelay.core/watchpoint :started #<State@5a42f4[user/config]: {...}>

This allows for more thorough logging for example, and it could even be used to deduce a dependency graph while starting a State. So, if you are using this extension point currently, you will have to update it a bit when upgrading to this new version.

Meta in place (breaking change)

The State object previously supported Clojure’s with-meta. Since this is a pure function - and was implemented as such - this resulted in a new State object. This could lead to undesirable and suprising effects. In version 2.0 support for with-meta has been replaced with Clojure’s alter-meta! and reset-meta!. These functions update the meta-data of State objects in place. No more suprises.

That’s it for the major changes to the library. The smaller additions, changes and fixes you can find in the changelog. The updated documentation can be found at cljdoc.

As always, have fun!

Clojure - Scala - Java - JavaEE - Datomic - Reagent - Figwheel - HugSQL - JavaScript - Node.js - Maven - SBT - XML - XSD - XSLT - JSON - jQuery - HTML - HTMX - React - Redux - OAuth - REST - GraphQL - ZooKeeper - Kafka - Akka HTTP - PostgreSQL - ElasticSearch - Cassandra - Redis - Mule - RabbitMQ - MQTT - SOAP - Linux - macOS - Git - Scrum - Emacs - Docker - Kubernetes - Ansible - Terraform - Jenkins - GitHub - GitLab - Devops - Raspberry Pi - Event Sourcing - Functional Reactive Programming - Ports and Adapters (Hexagonal)

Functional Bytes, 2013-2024


06 267 145 02

KvK: 59562722

Algemene voorwaarden