Documentation for namespace aliased keywords (particularly relevant for clojure.spec use)

1,228 views
Skip to first unread message

Dave Tenny

unread,
Feb 1, 2017, 7:12:35 PM2/1/17
to Clojure
Looking at the documentation for keywords under https://clojure.org/reference/reader#_literals
there is no mention for the syntax for namespace aliased keywords, and this is very important it you want to manage your keywords for clojure.spec.

I only know about it because google turned up some clojure.spec notes on it, but it seems it should be in the mainline clojure docs too.
I certainly didn't know about it until today.

Say we have this namespace:

(ns foo
 
(:require [clojure.spec :as s]))
(s/def ::specific-even-number #(= 1000 %))




And then in the user namespace:

(ns user)
(require '[clojure.spec :as s])
(require '
[foo :as f])


(s/valid? :f/specific-even-number 1000)
;; Exception Unable to resolve spec: :f/specific-even-number  clojure.spec/reg-resolve! (spec.clj:70)

What is needed is the extra colon, as if we're saying use the current namespace, only that isn't what happens.
(s/valid? ::f/specific-even-number 1000)
;; true

Perhaps the clojure docs are correctly stating the use of this kind of keyword my interpretation is just flawed, but it certainly wasn't obvious to me, and nobody in my team knew that we could do this either.  The result was some namespace unfriendly code to avoid typing in long namespaces on clojure spec keywords.  Now that we know ::ns-alias/keyword we can do better on our clojure.spec use.

(The above tested in clojure 1.8.0).

While I'm here, I want to say that I am _deeply_ disappointed that clojure.spec def/fdef forms don't support documentation strings.  I upvoted the issue in the bug tracker, but there were only 27 votes including mine when I checked on this half-year-plus old issue.


Dave Tenny

unread,
Feb 2, 2017, 6:58:56 AM2/2/17
to Clojure
I also have to wonder why keyword namespaces aren't processed like other symbol namespaces, that is, to interpret the namespace portion before a '/' with alias consideration.
No doubt there's a good answer, but it seems anachronistic that keywords need special syntax contortions to recognize namespace aliases when other symbols do not.
(Yes, clojure keywords may not be thought of as symbols, still, the rules for alias recognition are inconsistent).


Alex Miller

unread,
Feb 2, 2017, 11:21:04 AM2/2/17
to Clojure


On Wednesday, February 1, 2017 at 6:12:35 PM UTC-6, Dave Tenny wrote:
Looking at the documentation for keywords under https://clojure.org/reference/reader#_literals
there is no mention for the syntax for namespace aliased keywords, and this is very important it you want to manage your keywords for clojure.spec.

Agreed! This is definitely under doc'ed. I've added an issue to the site repo and I will work on that. Track at https://github.com/clojure/clojure-site/issues/164

I do want to clarify some terms. Keywords start with a ":". Auto-resolved keywords start with a "::". Generally, I prefer saying a keyword is "qualified" when has a namespace part. There is an unfortunate overlap in terms here where the namespace part of a keyword may or may not refer to an actual Clojure namespace, so it can be confusing to use the word "namespace" in this context (as it might imply more than it should). So I would say your question is really primarily about qualified auto-resolved keywords.
 
I only know about it because google turned up some clojure.spec notes on it, but it seems it should be in the mainline clojure docs too.
I certainly didn't know about it until today.

Auto-resolved and qualified auto-resolved keywords have been part of Clojure since very early days (pre 1.0 I believe) and have not really changed much if at all since then. 
 
Say we have this namespace:

(ns foo
 
(:require [clojure.spec :as s]))
(s/def ::specific-even-number #(= 1000 %))




And then in the user namespace:

(ns user)
(require '[clojure.spec :as s])
(require '
[foo :as f])


(s/valid? :f/specific-even-number 1000)
;; Exception Unable to resolve spec: :f/specific-even-number  clojure.spec/reg-resolve! (spec.clj:70)

What is needed is the extra colon, as if we're saying use the current namespace, only that isn't what happens.
(s/valid? ::f/specific-even-number 1000)
;; true
 
You also could have done (which is just what the above resolves to):

   (s/valid? :foo/specific-even-number 1000)

There are two (or maybe three depending how you look at it) kinds of auto-resolved keywords:

1) Unqualified (or simple) auto-resolved keywords like "::foo". These keywords are automatically resolved using the current *ns* into a qualified keyword, such as :user/foo (note that context matters).
2) Qualified auto-resolved keywords like "::a/foo". In this case the qualifier ("a") is resolved in terms of the namespace mappings of the current *ns*, so "a" will resolve to whatever "a" is aliasing (or an error if it is not a valid alias).
3) Kind of a special case of #2 is that qualifiers that are actual namespaces will also resolve in keywords like "::clojure.string/foo" which will resolve to ":clojure.string/foo". This is kind of weird and not something people typically do intentionally (so I'd recommend against it).

Perhaps the clojure docs are correctly stating the use of this kind of keyword my interpretation is just flawed, but it certainly wasn't obvious to me, and nobody in my team knew that we could do this either.  The result was some namespace unfriendly code to avoid typing in long namespaces on clojure spec keywords.  Now that we know ::ns-alias/keyword we can do better on our clojure.spec use.

(The above tested in clojure 1.8.0).

While I'm here, I want to say that I am _deeply_ disappointed that clojure.spec def/fdef forms don't support documentation strings.  I upvoted the issue in the bug tracker, but there were only 27 votes including mine when I checked on this half-year-plus old issue.

Well, no reason to be disappointed yet - spec is still a work in progress. That's actually one of the top voted issues in the whole system so I am well aware of it. I share the desire to see docstrings for specs (which we could leverage in "doc" - btw, you can (doc :foo/specific-even-number) now) and it's something Rich suggested to me in the early days of spec. The big question is how to implement it. There is not currently a good place to stash it so it just needs some analysis. It also opens up questions about other meta for specs (like we currently have some file/attributes in some cases that could be used to make "source" work with it - that would be handy if it always worked).

I also have to wonder why keyword namespaces aren't processed like other symbol namespaces, that is, to interpret the namespace portion before a '/' with alias consideration.
No doubt there's a good answer, but it seems anachronistic that keywords need special syntax contortions to recognize namespace aliases when other symbols do not.
(Yes, clojure keywords may not be thought of as symbols, still, the rules for alias recognition are inconsistent).

Regular keyword namespaces *are* processed like symbol namespaces and this (importantly) makes them _values_ that can be understood unambiguously independent of their processing context. Auto-resolved keywords are special in that the reader (which otherwise has very few ties to the outer environment) must reach into your current *ns* to understand how to auto-resolve the qualifier.

Note that because they depend on environment context during reading, auto-resolved keywords are NOT part of the edn spec and are not supported by the edn reader. This is a good example of why they are somewhat special.

I also would like to mention that there were two syntax enhancements made in Clojure 1.9 related to easing the use of qualified keywords:

- Map namespace literal syntax - a convenience for specifying the qualifier of all keyword/symbol keys for a map once, rather than once per key. The syntax here echoes the auto-resolved keyword syntax. See docs at: https://clojure.org/reference/reader#_maps
- Qualified key destructuring which similarly allows you to destructure a set of keys with the same qualifier and specify the qualifier once rather than once per key. See docs at: https://clojure.org/reference/special_forms#_map_binding_destructuring
Reply all
Reply to author
Forward
0 new messages