meander

markaddleman 2022-03-25T01:08:07.948389Z

I use the following idiom:

(m/rewrite [1 2 3]
   [(m/and !a1 !a2) ...]
   {:a1 [!a1 ...] :a2 [!a2 ...]})

🙌 2
enn 2022-03-25T17:38:59.597769Z

How can I bind nested values without failing if a parent is absent?

(m/match {:a 1}
  {:b {:c ?c}}
  ?c)
Execution error (ExceptionInfo) at clubhouse.resolvers.datalayer/eval641426 (REPL:203).
non exhaustive pattern match

Jimmy Miller 2022-03-25T17:40:14.444379Z

You can always add an extra case like _ nil or ?x ?x

Jimmy Miller 2022-03-25T17:41:20.741719Z

Or use m/or at the variable

enn 2022-03-25T17:43:59.410449Z

in my real use case, I still want to bind a bunch of other things at other levels, something like

(m/match {:a 1 :c {:foo 3}}
  {:a ?a
   :b ?b
   :c {:foo ?c}
   :d {:bar ?d}}
  [?a ?b ?c ?d])

enn 2022-03-25T17:45:07.973299Z

I thought I could do :d {:bar (m/or ?d (m/let [?d 1]))}, but that still gives me non-exhaustive pattern match. Is that not the right level for the m/or?

enn 2022-03-25T17:46:05.219059Z

ah, if I move m/or up a level it works

Jimmy Miller 2022-03-25T17:48:04.411609Z

Yeah at that level there would have to be a map at :d. The key couldn't be missing. Moving it up one level allows that.

enn 2022-03-25T20:24:19.800539Z

When matching a map, how can I distinguish between a key’s absence and its having the value nil ?

noprompt 2022-03-25T22:16:53.402359Z

This is one of those areas where I don't have a great answer. When I went to put together the map pattern matching semantics I based them on the observations that 1) find use is uncommon, and 2) most folks are concerned with observable equivalence and not semantic equivalence when it comes to get . The subtle difference between the following two expressions do not weight heavily on most. Its a pragmatic trade off I suppose.

(get {} :m)
(get {:m nil} :m)
Considering these observations, I chose to have the compiler use get instead of find and favor the common map access patterns. The nice thing is that it does help reduce the number of clauses you might write for map pattern matching. The drawback is exactly this case. 🙂

noprompt 2022-03-25T22:27:10.656949Z

If it is cheap, you could preprocess the map to have "semantic nils" i.e. ::blank / ::undefined and then pattern match on those. So

user=> (def blank (fnil identity ::blank))
#'user/blank
user=> (blank (get {:m nil} :m ::undefined))
:user/blank
user=> (blank (get {} :m ::undefined))
:user/undefined
user=> (blank (get {:m 10} :m ::undefined))
10
(`blank` is probably a terrible name but you get the idea.)

enn 2022-03-25T23:00:43.249799Z

Interesting, that makes sense. It’s good to know I’m not missing an obvious way to do this.

enn 2022-03-25T23:03:05.967689Z

Perhaps regrettably, we’ve got an API contract for updates where {:foo nil} means “unset the :foo property,” while the absence of :foo means “leave the :foo property as it is.”

noprompt 2022-03-25T23:08:10.622519Z

Ha! Yep. We have a situation like that with one of our APIs too. {:foo nil} is fine but if :foo is missing the API yells at you. 😐