# Efficiently updating nested data structures in Racket

**URL:** <https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877>\
**Category:** Questions & Answers\
**Tags:** data-structures\
**Created:** [April 22, 2024, 5:19pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877 "2024-04-22T17:19:28Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 22, 2024, 5:19pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/1 "2024-04-22T17:19:28Z")

</div>

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?

Regards.

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [April 22, 2024, 5:49pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/2 "2024-04-22T17:49:38Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [April 22, 2024, 6:07pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/3 "2024-04-22T18:07:31Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 22, 2024, 6:28pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/4 "2024-04-22T18:28:00Z")

</div>

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.

So I want to write something like this:

```scheme
(lens-transform (lens-thrush
                  (list-where (lambda (p) (and (starts-with? (person-name p) "R")
                                               (< (person-age p) 18))))
                  person-age-lens)
                (list (person "Rouan" 17))
                (lambda (age) (+ age 10)))

```

struct-copy is too clunky if you start having 3 or more levels of nesting.

I am used to using the Specter library in Clojure and it is really easy to work with nested data and do the things I am describing above.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [April 22, 2024, 7:06pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/5 "2024-04-22T19:06:52Z")

</div>

I am unfamiliar with Specter. Do you have some illustrative examples?

---

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 22, 2024, 7:42pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/6 "2024-04-22T19:42:18Z")

</div>

You can check out Specter here: [GitHub - redplanetlabs/specter: Clojure(Script)'s missing piece](https://github.com/redplanetlabs/specter).

To write the same example in Clojure using Specter, the code would look like this:

```scheme
(transform [ALL 
           (fn [p] (and (starts-with? (:name p) "R")
                        (< (:age p) 18)))
           :age]
           [(->Person "Rouan" 17)]
           (fn [age] (+ age 10)))

```

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.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [April 22, 2024, 8:10pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/7 "2024-04-22T20:10:04Z")

</div>

How are the nodes represented? As hash tables (maps) or structures with named fields?

---

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 22, 2024, 8:20pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/8 "2024-04-22T20:20:15Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [April 22, 2024, 9:05pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/9 "2024-04-22T21:05:40Z")

</div>

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++`.

[https://docs.racket-lang.org/struct-plus-plus/index.html](https://docs.racket-lang.org/struct-plus-plus/index.html)

---

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 23, 2024, 5:59am UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/10 "2024-04-23T05:59:51Z")

</div>

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 🙂

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [April 23, 2024, 3:56pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/11 "2024-04-23T15:56:31Z")

</div>

You can get a lot of mileage out of `match`.

```scheme
#lang racket

(struct person (name age) #:transparent)
(person "bob" 17)

(match (list (hash 'bob (person "Bob" 18) 'alice (person "Alice" 21)))
  [(list (hash-table ('bob (struct* person ([name name] [age age])))))
   (displayln (~a name " is " age " years old"))])

; Very minimal demo of struct++ , just because                                                  
(require struct-plus-plus)

(struct++ person2 ([name string?]
                   [age natural-number/c])
          #:transparent)
(person2++ #:name "Alice" #:age 18)

```

NB: The `hash-table` match pattern is deprecated; I need to finish updating my Racket to 8.12

---

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [April 24, 2024, 1:35am UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/12 "2024-04-24T01:35:59Z")

</div>

Always fun to do these; functions and some sugar 👌.

> <https://gist.github.com/bakgatviooldoos/f6e91ea926b0fec393c1067cd61bf738>

---

<div class="post-metadata">

**Author:** ![rvdalen](https://avatars.discourse-cdn.com/v4/letter/r/b4bc9f/32.png) [@rvdalen](https://racket.discourse.group/u/rvdalen)\
**Post date:** [April 27, 2024, 8:09pm UTC](https://racket.discourse.group/t/efficiently-updating-nested-data-structures-in-racket/2877/13 "2024-04-27T20:09:06Z")

</div>

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:

```scheme
#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.")))))

```

When I run the example above, I get the output:

```scheme
(list 10 20 (vector 100 (hash 'age 41 'book (book "Joe is smelling daisies") 'name "Rouan")))

```

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.
