Fork me on GitHub

With the namespaced maps functionality, is the intention that the namespaces refer to valid existing namespaces?


i.e. in the example: #:person{:first "Han" :last "Solo" :ship #:ship{:name "Millenium Falcon" :model "YT-1300f light freighter"}}, is the assumption that person and ship are actual namespaces, or is it considered ok that they just be ad-hoc qualifiers?


Just ad hoc qualifiers (although they can be namespaces).


That’s why there’s a JIRA issue open for allowing some sort of alias call for a non-existent namespace, so you can use it with :: keywords.


@seancorfield: Thanks - got a link for that JIRA?


Having said that, I can’t find the JIRA issue (hah! I knew you were going to ask!)


Does that mean that :: keywords and namespaced map forms do have to use aliases or valid namespaces?


I swear @alexmiller said that such an issue was under consideration...


Yeah, I remember something along those lines too.


Right now, you need a valid namespace to create an alias. Alex offered a workaround (that is used somewhere in core I think?) where you use in-ns to enter the non-existent namespace then in-ns to switch back to your own namespace, then you can create the alias.


So that actually creates the namespace.


That’s the current workaround 🙂


"Clojure/core-approved!" 😈


That’s pretty horrible, I’d better check what Cursive thinks of that, especially since it looks like those aliases will be used outside that ns.


This in one of our files where we’re using specs and namespaced keywords:

;; workaround aliasing restriction
(in-ns 'ws.domain.member)
(in-ns 'ws.domain.member.update)
(in-ns 'ws.domain.member.validation)
;; back to normal
(in-ns 'ws.profile.field)

(alias 'm  'ws.domain.member)
(alias 'mu 'ws.domain.member.update)
(alias 'mv 'ws.domain.member.validation)

Alex Miller (Clojure team)01:08:29

It is under consideration but I don't think there is a public jira


@alexmiller: Is the current expectation that :: keywords and maps do use valid aliases or namespaces?


i.e. should I be flagging it if the user used #::ship{:name "Millenium Falcon" :model "YT-1300f light freighter”} where ship is not a valid alias or namespace?


It needs to be a valid alias


The reader blows up otherwise:

boot.user=> #::ship{:foo :bar}

clojure.lang.LispReader$ReaderException: java.lang.RuntimeException: Unknown auto-resolved namespace alias: ship
             java.lang.RuntimeException: Unknown auto-resolved namespace alias: ship
clojure.lang.LispReader$ReaderException: java.lang.RuntimeException: Unmatched delimiter: }
             java.lang.RuntimeException: Unmatched delimiter: }


Or namespace: ::clojure.string/foo is fine


Ok, thanks.


boot.user=> (in-ns 'ship)
:foo:bar#object[clojure.lang.Namespace 0x74bf3db1 "ship"]
ship=> (in-ns 'boot-user)
#object[clojure.lang.Namespace 0x215119de "boot-user"]
boot-user=> #::ship{:foo :bar}
#:ship{:foo :bar}

Alex Miller (Clojure team)01:08:27

Right, can be fully qualified

Alex Miller (Clojure team)01:08:12

Basically the namespace part is resolved in the current ns


Ok. But the same is not true of #: forms, they can have some arbitrary disambiguator?

Alex Miller (Clojure team)02:08:55

If you mean namespaced maps they follow the same rules

Alex Miller (Clojure team)02:08:19

Has to be either fully qualified or an alias in current ns


@alexmiller: So in #:ship{:name "Millenium Falcon" :model "YT-1300f light freighter”} ship must be a valid namespace?


I mean namespaced but not auto-resolved maps.


No, we showed that above.

Alex Miller (Clojure team)02:08:38

It must be either a fully qualified ns or a valid alias in current ns


Even in the non-auto-resolve case?


i.e. #: as opposed to #::?

Alex Miller (Clojure team)02:08:14

I don't know what you mean by that


#:anything{:foo :bar} is valid right?

Alex Miller (Clojure team)02:08:27

Those are both auto resolved


Ah, I see the confusion. That produces {:anything/foo :bar} which is fine.


The JIRA makes it sound like :: forms are “auto-resolved"


So the result is fully qualified.


I’m trying to figure out if only “auto-resolved” forms are required to use a valid alias or namespace.


But if you use :: on a key it will auto-resolve

Alex Miller (Clojure team)02:08:09

Using same rules as :: keywords


So, is it valid to say #:anything{} where anything is not a valid alias or ns?


So #: as a prefix is a red herring here. Only :: matters. For your question.


It seems to work currently, my question is if it should.


The alias or valid namespace issue only applies to ::


Right. So for both maps and keywords, using the :: form means that the following name must refer to an alias or namespace, but for : forms it can be anything you like.


And there's #:ship{:name ".." ::thing 42} as well, right? Where ::thing will auto-resolve.


Right. In the map case where the keyword actually specifies a ns, that ns will be used (taking into account the _ case too)


So in boot.user=> #:ship{:name ".." ::thing 42} will produce {:ship/name ".." :boot.user/thing 42}


Did the _ thing get in? Hmm, let me try...


According to the JIRA it did, yeah.


Yeah, :_/a produces :a


(inside a ns-qualified map literal)


boot.user=> #:ship{:name "..." ::thing 42 :_/a 1}
{:ship/name "...", :boot.user/thing 42, :a 1}


Is all of this documented somewhere, hopefully? i.e. what is supposed to be accepted, and for bonus points whether the implementation accepts things that are not supported?


The one case that is confusing is the #:foo{} case, where the JIRA uses the word “namespace” but foo is not actually required to be one.


It’s overloading the term “namespace”, basically.


So that case is something that could be documented more explicitly? (assuming it is intended to be accepted, not an accident of the current implementation)


Yes, I think so


(as someone needing to understand how it worked)


Is there any known resolution regarding (defn ^long bad [x] (inc x)) vs (defn good ^long [^long x] (inc x))? I know the second approach is the proper one, but if you mistakenly apply the first one - there's nothing to warn you that you might get a clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException: Unable to resolve classname: clojure.core$long@414dde97 later on


when doing (* 1 (bad 1))


the explicit {:tag 'long} approach got fixed in 1.8 (

Alex Miller (Clojure team)12:08:01

The issue with the dual meaning of "namespace" (as a naming qualifier in keywords and symbols) and as a named var container is not unique to here. older Clojure docs refer to the latter as libraries, which is equally confusing in a different way. Not enough words.

Alex Miller (Clojure team)12:08:20

@dm3 what resolution are you looking for? In general type hints are hints and this ignored if not applicable. This is a bit different in that it's a hint of an invalid form, not a hint that doesn't apply. I think may be applicable


yep, that applies


Regarding primitives support in Clojure, I'm guessing the 4 argument restriction was to avoid generating too many interfaces. Was an alternate design considered of dynamically generating interfaces based on what combinations of primitive arguments are actually used throughout a project?


So if only usage of primitives is [a1 ^long a2 a3 a4 a5] the only interface generated is clojure.lang.IFn$OOLOOO

Alex Miller (Clojure team)13:08:47

don’t know, that was long before my involvement


@dm3: the Eastwood linter helps detect some incorrect type hints, and I think the one in your example, too.


yep, that's where I found an explanation


@nathanmarz: The problem is that Clojure in general does not assume global knowledge of the project


I guess you could have a global cache of type signature->interface, and that would be populated as you compiled forms


I looked at doing exactly that for a while, but you quickly run into problems with naming the generated interfaces, especially if you allow the generation of typed invoke signatures for arbitrary user types.


The JVM gives you a 65535 chr budget for class/interface names, so for the most part you can just splat fully qualified type names together, but I never got past a proof of concept using short class names and the hash of the full thing which was problematic from a repeatability/caching perspective. For binary compat your generated invoke interface names need to be fully deterministic. I know @bronsa looked at this too at some point, don't think he took it any further.


@cfleming @arrdem Yea the caching approach is what I was thinking of, and you could just limit the types to objects, doubles, and longs. Pushing user types down to the bytecode level seems like a separate issue.


I tried that a while ago, unfortunately it doesn't go well when AOT comes into play


I think it should be an easier way to extend over the 4args support by reducing the number of combinations, making eg IFn$LOLD , IFn$LLOD, $LDOL and so on map to the same interface and have the compiler just reorder args at compile time but I never tried implementing it (using this approach the current number of prim interfaces would go from 358 to 100 and adding a 5th arg would go from 100 to 162 rather than from 358 to 1086, we could support 7 args with just 352 interfaces)

Alex Miller (Clojure team)23:08:10

I suspect there’s probably a much better approach using invokedynamic that would let you do this stuff dynamically without all the interfaces