Welcome! Please see the About page for a little more info on how this works.

+1 vote
ago in Test by

While learning about the new support for checked :keys!, I came across this test case for nested maps: https://github.com/clojure/clojure/blob/master/test/clojure/test_clojure/data_structures.clj#L1382-L1390.

I have two questions.

First, does this test imply that :keys! will also be supported in nested destructuring?

Second, I think there may be a small issue with one of the test cases. It checks that the following destructuring throws an exception:

{a :a {aa :a :as m :keys! [b c & d e]} :b} (dissoc sample-map :c)

The test passes, but it's not obvious whether that's because :c has been removed from the map or because :e is listed in :keys! without being present. Since the previous test doesn't include :e, it's a bit ambiguous which condition is actually being exercised.

To make the intent clearer, would it make sense to split this into two separate test cases?

One that removes :c and uses :keys! [b c & d], verifying that the missing required key causes the exception.
Another that keeps :c but uses :keys! [b c & d e], verifying that the non-bound but missing :e is what triggers the exception.

1 Answer

+2 votes
ago by

If we try that code in a REPL, we see that it complains about :e being missing:

user=> (let [sample-map {:a 1 :b {:a 2 :b 3 :c 4 :d 42}}
             {a :a {aa :a :as m :keys! [b c & d e]} :b} (dissoc sample-map :c)] b)
Execution error (IllegalArgumentException) at user/eval126 (REPL:1).
Missing required key: :e

But the test is weird because sample-map doesn't actually have a :c key at the top level, only nested under :b.

In answer to your overall question, yes, :keys! works anywhere that :keys already works, and that includes nested map destructuring.

Here's an example from our code at work:

  [{:keys! [worldsingles-application members actioner database]
    {:keys! [& redis-pubsub] pubsub :redis-pubsub} :worldsingles-application
    :as service}]

This checks that :redis-pubsub is a key under :worldsingles-application which is a key of service, but does not bind redis-pubsub, and also binds pubsub to the :redis-pubsub key. Yes, we have Alpha 2 in production and this code used to be multiple let bindings and explicit checks for keys being present.

...