Skip to main content
added 474 characters in body
Source Link
blubb
  • 123
  • 5

I am learning Clojure by implementing a simplistic resource reservation system. At its core are the two functions for placing/canceling reservations for a given resource:

; thread-safe map of {resource -> #{reservations}}
(def known-reservations (atom {}))

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(merge-with clojure.set/union % {(:resource new-res) #{new-res}})))

(defn cancel-reservation
  [canceled-res]
  (swap! known-reservations
         #(update % (:resource canceled-res) disj canceled-res)))

They seem to work correctly, but the lack of symmetry bothers me. Placing and canceling reservations are symmetric operations, but this is not reflected by the above code: merge-with clojure.set/union doesn't even resemble update ,,, disj.

I tried the following variant:

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(update % (:resource new-res) conj new-res)))

but since conj nil :x returns a list rather than a set, this leads to duplicate reservations which I want to prevent.

What is the most idiomatic fix to my problem?

PS: if anyone can come up with a more expressive question title, I'll thankfully accept the edit!

edit: here's a simple usage example for experimentation:

(defrecord Resource [name])
(defrecord Reservation [^Resource resource user])

(def car (->Resource "car"))
(def my-res (->Reservation car "me"))
(def your-res (->Reservation car "you"))

(place-reservation my-res)
(place-reservation your-res)
(place-reservation my-res) ;; duplicate reservation must not change anything

(cancel-reservation my-res)

I am learning Clojure by implementing a simplistic resource reservation system. At its core are the two functions for placing/canceling reservations for a given resource:

; thread-safe map of {resource -> #{reservations}}
(def known-reservations (atom {}))

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(merge-with clojure.set/union % {(:resource new-res) #{new-res}})))

(defn cancel-reservation
  [canceled-res]
  (swap! known-reservations
         #(update % (:resource canceled-res) disj canceled-res)))

They seem to work correctly, but the lack of symmetry bothers me. Placing and canceling reservations are symmetric operations, but this is not reflected by the above code: merge-with clojure.set/union doesn't even resemble update ,,, disj.

I tried the following variant:

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(update % (:resource new-res) conj new-res)))

but since conj nil :x returns a list rather than a set, this leads to duplicate reservations which I want to prevent.

What is the most idiomatic fix to my problem?

PS: if anyone can come up with a more expressive question title, I'll thankfully accept the edit!

I am learning Clojure by implementing a simplistic resource reservation system. At its core are the two functions for placing/canceling reservations for a given resource:

; thread-safe map of {resource -> #{reservations}}
(def known-reservations (atom {}))

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(merge-with clojure.set/union % {(:resource new-res) #{new-res}})))

(defn cancel-reservation
  [canceled-res]
  (swap! known-reservations
         #(update % (:resource canceled-res) disj canceled-res)))

They seem to work correctly, but the lack of symmetry bothers me. Placing and canceling reservations are symmetric operations, but this is not reflected by the above code: merge-with clojure.set/union doesn't even resemble update ,,, disj.

I tried the following variant:

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(update % (:resource new-res) conj new-res)))

but since conj nil :x returns a list rather than a set, this leads to duplicate reservations which I want to prevent.

What is the most idiomatic fix to my problem?

PS: if anyone can come up with a more expressive question title, I'll thankfully accept the edit!

edit: here's a simple usage example for experimentation:

(defrecord Resource [name])
(defrecord Reservation [^Resource resource user])

(def car (->Resource "car"))
(def my-res (->Reservation car "me"))
(def your-res (->Reservation car "you"))

(place-reservation my-res)
(place-reservation your-res)
(place-reservation my-res) ;; duplicate reservation must not change anything

(cancel-reservation my-res)
Source Link
blubb
  • 123
  • 5

Adding/removing map values: unsymmetric implementation

I am learning Clojure by implementing a simplistic resource reservation system. At its core are the two functions for placing/canceling reservations for a given resource:

; thread-safe map of {resource -> #{reservations}}
(def known-reservations (atom {}))

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(merge-with clojure.set/union % {(:resource new-res) #{new-res}})))

(defn cancel-reservation
  [canceled-res]
  (swap! known-reservations
         #(update % (:resource canceled-res) disj canceled-res)))

They seem to work correctly, but the lack of symmetry bothers me. Placing and canceling reservations are symmetric operations, but this is not reflected by the above code: merge-with clojure.set/union doesn't even resemble update ,,, disj.

I tried the following variant:

(defn place-reservation
  [new-res]
  (swap! known-reservations
         #(update % (:resource new-res) conj new-res)))

but since conj nil :x returns a list rather than a set, this leads to duplicate reservations which I want to prevent.

What is the most idiomatic fix to my problem?

PS: if anyone can come up with a more expressive question title, I'll thankfully accept the edit!