Clojure

Support destructuring maps with namespaced keywords

Details

  • Type: Enhancement Enhancement
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: Release 1.5
  • Fix Version/s: Release 1.6
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code and Test
  • Approval:
    Ok

Description

Current :keys destructuring expects symbols and creates local bindings based on those symbols. This works fine with maps that use non-namespaced keyword keys. This enhancement is to add support for destructuring maps with namespaced keyword keys.

;; typical key destructuring for keyword keys without namespaces
(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))

;; WANT some way to destructure map with namespaced keys
(let [{:keys [????]} {:x/a 1 :y/b 2}] (+ a b))

Approach: Allow keywords (with or without namespaces) in :keys destructuring. Destructure to bindings with the name of the keyword (namespace is ignored).

;; this now works
(let [{:keys [x/a y/b]} {:x/a 1 :y/b 2}] (+ a b))

;; add support for putting keywords into :keys as well to support ::keywords
(let [{:keys [:x/a :y/b]} {:x/a 1 :y/b 2}] (+ a b))
(let [{:keys [::a]} {:user/a 1}] a)

;; syms will also now support namespaced symbols
(let [{:syms [x/a y/b]} {'x/a 1 'y/b 2}] (+ a b))

Patch: clj-1318-6.diff

Screened by: Stuart Sierra. See comments, below.

Doc TODO: Will need to update http://clojure.org/special_forms#binding-forms with new binding form.

  1. clj-1318-6.diff
    24/Jan/14 11:07 AM
    4 kB
    Alex Miller
  2. clj-1318-5.diff
    24/Jan/14 10:28 AM
    4 kB
    Alex Miller
  3. clj-1318-4.diff
    10/Jan/14 2:24 PM
    4 kB
    Alex Miller
  4. clj-1318-3.diff
    07/Jan/14 4:40 PM
    3 kB
    Alex Miller
  5. clj-1318-2.diff
    06/Jan/14 3:06 PM
    3 kB
    Alex Miller
  6. clj-1318-1.diff
    06/Jan/14 11:16 AM
    2 kB
    Alex Miller

Activity

Hide
Nicola Mometto added a comment -

Why {:keys [:a/b]} and not {:keys [a/b}}?
Also, this should probably be extended to :syms for consistency

Show
Nicola Mometto added a comment - Why {:keys [:a/b]} and not {:keys [a/b}}? Also, this should probably be extended to :syms for consistency
Hide
Alex Miller added a comment -

Good questions both. For the first question, we want to make locally namespaced keywords (::foo) work and there is no way to say that as a symbol.

I am waiting to hear back from Rich whether support for namespaced :syms is desirable. I think the change to support it is identical to the change to support namespaced keywords as symbols. I'm going to proactively update the patch to support that too.

Show
Alex Miller added a comment - Good questions both. For the first question, we want to make locally namespaced keywords (::foo) work and there is no way to say that as a symbol. I am waiting to hear back from Rich whether support for namespaced :syms is desirable. I think the change to support it is identical to the change to support namespaced keywords as symbols. I'm going to proactively update the patch to support that too.
Hide
Alex Miller added a comment -

Added new patch - now supports namespaced symbols or keywords in :keys and namespaced symbols in :syms.

Show
Alex Miller added a comment - Added new patch - now supports namespaced symbols or keywords in :keys and namespaced symbols in :syms.
Hide
Rich Hickey added a comment -

Should (also) support symbols for names, e.g. {:keys [a/b]}, only limitation is you can't get ns alias resolution. :syms support makes sense, but may seem weird to provide keywords for local names (where it doesn't as much for keywords), but would allow reaching aliases. My preference is no keyword names support for :syms, i.e. {:syms [a/b]} ok, {:syms [:a/b]} not.

Show
Rich Hickey added a comment - Should (also) support symbols for names, e.g. {:keys [a/b]}, only limitation is you can't get ns alias resolution. :syms support makes sense, but may seem weird to provide keywords for local names (where it doesn't as much for keywords), but would allow reaching aliases. My preference is no keyword names support for :syms, i.e. {:syms [a/b]} ok, {:syms [:a/b]} not.
Hide
Nicola Mometto added a comment -

To me {:syms [:a/b]} doesn't feel any more weird than writing {:keys [:a/b]}.
If this is going to be added, I think it should be consistent for :keys and :syms.
I understand that :syms is rarely used and this should not be an issue realistically, but I would expect everything that works for :keys to work for :syms too and adding only half a feature to :syms might cause unnecessary confusion.

Show
Nicola Mometto added a comment - To me {:syms [:a/b]} doesn't feel any more weird than writing {:keys [:a/b]}. If this is going to be added, I think it should be consistent for :keys and :syms. I understand that :syms is rarely used and this should not be an issue realistically, but I would expect everything that works for :keys to work for :syms too and adding only half a feature to :syms might cause unnecessary confusion.
Hide
Nicola Mometto added a comment -

With this patch this will now work:

user=> (let [:a/b 1] b)
1

I don't think this is desiderable.

Show
Nicola Mometto added a comment - With this patch this will now work:
user=> (let [:a/b 1] b)
1
I don't think this is desiderable.
Hide
Alex Miller added a comment - - edited

Right, that is a consequence of allowing keywords in the :keys. At a glance this seems hard to address without significant changes unless we catch it prior to processing. Will consider.

Show
Alex Miller added a comment - - edited Right, that is a consequence of allowing keywords in the :keys. At a glance this seems hard to address without significant changes unless we catch it prior to processing. Will consider.
Hide
Alex Miller added a comment -

Added new patch variant that catches keywords as let binding keys and throws an Exception.

Show
Alex Miller added a comment - Added new patch variant that catches keywords as let binding keys and throws an Exception.
Hide
Alex Miller added a comment -

Added one test in -4 showing example of auto-resolved keywords in :keys.

Show
Alex Miller added a comment - Added one test in -4 showing example of auto-resolved keywords in :keys.
Hide
Stuart Sierra added a comment -

Screened. A few comments:

1. The examples in the tests use {:keys (a b)} with lists instead of {:keys [a b]} with vectors. Both forms are accepted both before and after the patch, but the docs at Clojure - special_forms only show vectors.

2. I would like this to work, but it would add some complexity:

(ns com.example.myproject.foo)

  (def data
    {::a 1 ::b 2})

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (ns com.example.myproject.bar
    (:require [com.example.myproject.foo :as foo]))

  ;; I would like this to work:
  (let [{:keys [foo/a foo/b]} foo/data]
    [a b])
  ;;=> [nil nil]

  ;; This is good enough, however:
  (let [{:keys [::foo/a ::foo/b]} foo/data]
    [a b])
  ;;=> [1 2]

3. This doesn't produce an error, which is logically consistent but perhaps not desirable:

(let [{:a ::foo/a} foo/data]
    [a])
Show
Stuart Sierra added a comment - Screened. A few comments: 1. The examples in the tests use {:keys (a b)} with lists instead of {:keys [a b]} with vectors. Both forms are accepted both before and after the patch, but the docs at Clojure - special_forms only show vectors. 2. I would like this to work, but it would add some complexity:
(ns com.example.myproject.foo)

  (def data
    {::a 1 ::b 2})

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (ns com.example.myproject.bar
    (:require [com.example.myproject.foo :as foo]))

  ;; I would like this to work:
  (let [{:keys [foo/a foo/b]} foo/data]
    [a b])
  ;;=> [nil nil]

  ;; This is good enough, however:
  (let [{:keys [::foo/a ::foo/b]} foo/data]
    [a b])
  ;;=> [1 2]
3. This doesn't produce an error, which is logically consistent but perhaps not desirable:
(let [{:a ::foo/a} foo/data]
    [a])
Hide
Rich Hickey added a comment -

please change the tests to use vectors

Show
Rich Hickey added a comment - please change the tests to use vectors
Hide
Alex Miller added a comment -

Added new -5 diff that uses vectors instead of lists in :keys tests.

Show
Alex Miller added a comment - Added new -5 diff that uses vectors instead of lists in :keys tests.
Hide
Alex Miller added a comment -

And also fixing :syms [] in -6 diff.

Show
Alex Miller added a comment - And also fixing :syms [] in -6 diff.
Hide
Alex Miller added a comment -

Changed examples in description to use [].

Show
Alex Miller added a comment - Changed examples in description to use [].
Hide
Fogus added a comment -

A potential point of confusion here is illustrated by the following:

(let [m {:x/a 1, :y/b 2, :x/b 3000}
        {:keys [x/a y/b x/b]} m]
  (+ a b))

//=> 3

To get the answer 3001 one needs to remove the conflicting binding :y/b. Maybe this is not a big deal, but expect questions for the next 100 years.

Show
Fogus added a comment - A potential point of confusion here is illustrated by the following:
(let [m {:x/a 1, :y/b 2, :x/b 3000}
        {:keys [x/a y/b x/b]} m]
  (+ a b))

//=> 3
To get the answer 3001 one needs to remove the conflicting binding :y/b. Maybe this is not a big deal, but expect questions for the next 100 years.
Hide
David Nolen added a comment -

Ported to ClojureScript with CLJS-745

Show
David Nolen added a comment - Ported to ClojureScript with CLJS-745

People

Vote (0)
Watch (3)

Dates

  • Created:
    Updated:
    Resolved: