I am trying to write some code that updates immutable nested data structures.
There does not seem to be good support for this in base Racket, and the internet seems to point to using the lens library for this.
The lens library looks like it has not been updated in quite a while and seems to be missing obvious functions.
Is that my only solution for working with deeply nested immutable data structures or is there a different mechanism or library that I could have a look at?
It's not hard to whip up a function or macro that turns (nested-update table key1 key2 key3 new-value) into a series of hash-update calls. Or sets or what have you.
If it’s truly deeply nested, the lens library sounds appropriate to me.
Can you elaborate what you meant by “missing obvious functions”?
Note that “it has not been updated in quite a while” doesn’t necessarily mean it’s abandoned. It could simply be that the library is matured, so there’s nothing to be done.
For structs, also take a look at struct-copy, which is a bit clunky, but might suffice for your purposes and doesn’t require installing an external library.
One thing I do often is to update a subset of values in a list based on some predicate. For example I need to be able to express: Update each person's age field where the name starts with "R" and the age < 18.
Which essentially means, visit all the nodes in the vector which match the predicate, and for each node update the :age field by running the supplied update function.
In my example, I have a vector of structs with named fields (or records in Clojure terms). But that is just because of the example I chose. I can have arbitrary complex nested data structures of different types.
I was actually curious about the Clojure representation.
In Racket, for structs the field names aren't present at runtime.
Since hash tables store the keys at runtime, it's easier to make a "Spector for Racket" if only vectors and hash tables are used.
An alternative is to look at packages uses at @dstorrs package struct++.
Thanks, I will take a look at the struct++ package.
In the mean time I was thinking of writing a macro that takes a path description and converts it into the normal struct-copy and other base features so I don't have to write the clunky code by hand
Ok, so I spoke to the creator of lens library and @Jack said that what I am trying to do is traversal and not a lens so the functionality is not technically missing.
So I ended up writing my own library for this just as a proof-of-concept and here is an example of what it looks like to use the lib:
#lang racket/base
(require
sunshine-fw/slib/data
racket/function)
(struct book
(title)
#:transparent)
(update
; Specify the path through the nested data structures we want to update.
(list (ref/list 2) (ref/vec 1) (ref/hash 'book) (ref/struct-m book title))
; Define how we want to update the places we identify via the path above.
(const "Joe is smelling daisies")
; This is the nested data structure to update.
(list 10 20 (vector 100 (hash 'name "Rouan" 'age 41 'book (book "Smelling daisies.")))))
Because this is a proof-of-concept, there is much room for improvement, but I am happy with the interface so far and can use this already in my own code to help with selecting or updating values in nested data structures without needing to manually write the clunky code using Racket's built-in facilities for updating immutable data structures.