greghendershott / rackjure

Provide a few Clojure-inspired ideas in Racket. Where Racket and Clojure conflict, prefer Racket.
BSD 2-Clause "Simplified" License
236 stars 17 forks source link

`dict-*-in`? #59

Open erkin opened 3 years ago

erkin commented 3 years ago

I must say I love rackjure but there's something that's dearly missing for me. In Clojure you can use update-in to update nested maps (which are very common when doing state-based programming), such as

(update-in {:a 5 :b {:foo 10 :bar {:x 15}}} [:b :bar :x] inc)
;=> {:a 5 :b {:foo 10 :bar {:x 16}}}

This is very burdensome to do with Racket, such as

(dict-update {'a 5 'b {'foo 10 'bar {'x 15}}} 'b (λ (b) (dict-update b 'bar #λ(dict-update % 'x add1))))
;=> '((a . 5) (b (foo . 10) (bar (x . 16))))

I think something like a dict-update-in would be a great addition to rackjure.

(dict-update-in {'a 5 'b {'foo 10 'bar {'x 15}}} '(b bar x) add1)

Likewise dict-ref-in (get-in), dict-set-in (assoc-in) and dict-remove-in (dissoc-in).

erkin commented 3 years ago

This is the naïve implementation I use in my projects:

(define (dict-ref-in dict keys (not-found #f))
  (if (null? keys)
      dict
      (dict-ref-in (dict-ref dict (car keys)) (cdr keys) not-found)))

(define (dict-update-in dict keys function . args)
  (dict-update dict (car keys)
               (if (null? (cdr keys))
                   (λ (v) (apply function v args))
                   (λ (v) (apply dict-update-in v (cdr keys) function args)))))

(define (dict-set-in dict keys value)
  (dict-update-in dict keys (thunk* value)))
greghendershott commented 3 years ago

Although I'm not actively working on rackjure lately (ergo this slow reply):

I just wanted to point out that it arranges for Racket dictionaries to be applicable -- which enables using the threading macro ~> to flatten nested references like that. See https://docs.racket-lang.org/rackjure/index.html#%28part._dict-app%29.

greghendershott commented 3 years ago

Derp. Just after posting that I realize you're talking about updating.