How to find the fully qualified namespace of a symbol? - clojure

If I have a symbol who's namespace is an alias, like q/w, how can I find its actual namespace, say actual.namespace/w ?
I know that resolve will give me the fully qualified var, but I don't know how to get the namespace of a var.
The best I can do is:
(defn fqns [s] (str (get (ns-aliases *ns*) (symbol (namespace s)))))
surely there's a simpler way ?

You can get the namespace object of a symbol as shown below (if you want name of ns as string then just call str at the end):
(defn fqns [s] (->> (resolve s) meta :ns))

The symbol itself has no direct connection to any namespace object; its namespace part is just an interned string. It is only when it's being resolved that this string is treated as the name of an actual namespace in which to look up a Var. (In particular, you can create a symbol foo/bar without creating a namespace foo or registering foo as an alias for a namespace.) Thus, you need to either go through resolve or do part of its work yourself.
In the first case, you can say (in recent versions of Clojure; or you can skip the hyphens signifying property access -- instead of -ns, use ns -- to be backwards compatible)
(.. (resolve s) -ns -name)
This resolves a Var object, then extracts a reference to its namespace from its ns field and finally extracts the symbolic name of the namespace from its name field. (Note that the namespace and name functions wouldn't work, since Vars and namespaces do not implement clojure.lang.Named.)
The function from the question text is fine too, although it'll be better to extract the symbol name from the namespace -- (.-name (get ...)) -- rather than rely on the toString representation of namespaces through the use of str.
You may also want to add clojure.lang.Var and clojure.lang.Namespace type hints to avoid reflection in your calls. (If you want them to be faster, or if you need *warn-on-reflection* to tweak something else for performance and wish to avoid useless warnings here.)
If you wish to handle the possibility that the namespace does not exist (and return nil in that case, in keeping with usual Clojure idiom):
(defn fqns
([s]
(fqns (.-name *ns*) s))
([ns s]
(some-> ns
find-ns
.-name
ns-aliases
(get (symbol (namespace s)))
.-name)))
Or use some if-lets in Clojure 1.4 and earlier.

Related

Why Doesn't Clojure provide standard library after creating new namespace?

I came across this problem after creating a new namespace.
Here is the code:
(create-ns 'my-new-ns)
=> #object[clojure.lang.Namespace 0x7c7c8359 "my-new-ns"]
(in-ns 'my-new-ns)
=> #object[clojure.lang.Namespace 0x7c7c8359 "my-new-ns"]
(reduce + [1 2 3])
CompilerException java.lang.RuntimeException: Unable to resolve symbol: reduce in this context, compiling:(/private/var/folders/pg/bynypsm12nx1s4gzm56mwtcr0000gn/T/form-init1425759900088902804.clj:1:1)
As you can see reduce function is not defined in my-new-ns namespace.
I should be able to create new namespaces so What would be the best solution for this problem?
P.S: Also, I'm trying to create those namespaces for my users so they will be able to do whatever they want in their namespaces(the idea is like a container) and creating isolation between namespaces.
clojure.core functions are not special in their need to be referred to make them available for unqualified use. The ns macro does several things:
creates the namespace - create-ns
changes the current namespace to that namespace - in-ns
automatically refers all of the clojure.core vars into the new namespace - refer-clojure
You can always use the qualified form of the core function (unqualified is just less typing), so when you get in this situation, this simple call will get you right again:
(clojure.core/refer-clojure)
Instead of creating namespace manually and then switching to it, I'd recommend using ns macro. According to the doc:
Sets *ns* to the namespace named by name (unevaluated), creating it if needed.
Also it will load all public vars from clojure.core to newly created namespace.
So, basically this
> (create-ns 'my-new-ns)
> (in-ns 'my-new-ns)
> (clojure.core/refer 'clojure.core)
is equal to this
> (ns my-new-ns)
Update:
Answering your question: symbols from standard library are not referred in newly created namespace, that's why you cannot access them without qualifier:
> (create-ns 'x)
> (in-ns 'x)
> reduce ;; throws "Unable to resolve symbol"
> clojure.core/reduce ;; works fine
You need to refer those symbols manually by calling (clojure.core/refer 'clojure.core).
Your new namespace needs to use the "standard" namespaces to be able to resolve names in them. The documentation indicates that this would be java.lang, clojure.lang.Compiler and clojure.core.
I guess you are supposed to use
(ns my-new-ns)
instead. create-ns is a low-level facility.

Symbol is associated with the wrong namespace in a macro

I have this macro:
(defmacro widget [msg-type value & app-key]
`(defrecord ~msg-type [~value]
Message
(~'process-message [msg# app#]
(let [state# (~#app-key app#)]
(dissoc
(->>
(merge state# msg#)
(assoc app# ~#app-key))
:errors)))))
Message is a protocol defined in a clojurescript dependency, with a process-message function.
When I try to use widget like so
(ns my.cljs.ns
(:require-macros [my.ns.macros :as macro])
(:require [petrol.core :refer [Message]]))
(macro/widget A-Record a-field :a-key)
I get this error:
Bad method signature in protocol implementation,
my.ns.macros/Message does not declare method called
process-message ...
How can I get Message to refer to petrol/Message instead of my.ns.macros/Message?
You need the power of the mystical ~' operator :)
I see you already invoked it for process-message, so perhaps you are already acquainted with why; but for the purposes of the answer, stuff in the backtick gets fully namespace qualified, where as evaluate quote puts the literal symbol in place.
(macroexpand-1 '(widget :a :b))
And the error message indicate that you need to ~'Message if you want to avoid it having the current ns attached to it.
However fully qualifying Message with the petrol namespace would be a good move IMO
petrol.core/Message
That way you don't need to rely on it being referred in the ns declaration. Note you don't need to ~' it either.
Also I would be wary of (~#app-key app#) because app-key are optional... you could get nothing passed in which would call whatever #app is, which doesn't sound like something you want to happen. Similarly passing more than one seems wierd to. Maybe it should be a required param?

How to fully qualify a var's value?

Imagine that:
(def my-var 'my-symbol) ;; Please note that it must be 'my-symbol not `my-symbol
my-var ;; => my-symbol
But I want
;; => fully-qualified/my-symbol
Other than converting values to strings, is it possible to fully qualify my-var's value? Thanks.
Use a back quote instead of a straight quote:
(def my-var `my-symbol) ; and not 'my-symbol
Since symbols are created from strings (see here) and they are immutable, in order to build a fully qualified symbol from a symbol a conversion to String is inevitable.
(symbol (name (ns-name *ns*)) (name 'my-symbol))
Keywords are also able to be fully qualified. The value ::my-keyword will be expanded by the reader to be fully qualified, taking the namespace where the value is being read.
::my-keyword
;;= :user/my-keyword
(ns other-ns)
::my-keyword
;;= :other-ns/my-keyword
They are more broadly used in Clojure when you need fully qualified values.

Avoid overriding variable names

On a particular namespace I am working on I am beginning to run out of function names. Is there a way to get a warning like the one I get if I override a symbol from another namespace if I reuse a symbol which is already bound to a function in the same namespace?
If this is enough of a problem that you'd be willing to replace a (set of) core macro(s), you could try this approach:
(ns huge.core
(:refer-clojure :exclude [defn]))
(defmacro defn [name & defn-tail]
(assert (nil? (resolve name))
(str "Attempting to redefine already defined Var "
"#'" (.name *ns*) "/" name))
`(clojure.core/defn ~name ~#defn-tail))
Then any attempt to redefine an existing Var with defn will fail:
user=> (defn foo [] :foo)
#'user/foo
user=> (defn foo [] :bar)
AssertionError Assert failed: Attempting to redefine already defined Var #'user/foo
(nil? (resolve name)) user/defn (NO_SOURCE_FILE:2)
You could similarly replace defmacro; in that case you would have to call clojure.core/defmacro when defining your own variant.
Plain, unadorned def is a special form and receives magic treatment from the compiler, so you could still overwrite existing Vars with it. If you would like to guard against name clashes on that flank too, you could switch to something like defvar (used to be available in clojure.contrib.def) with a similar custom assert.
This isn't quite an answer to your question but may help avoid the issue depending on how the functions in your namespace are being used. You could make them into local functions using letfn, allowing you to reuse names for functions that are only used within the context of another function.
(defn main-fn [x]
(letfn [(secondary-fn [x] (* x x))
(another-fn [x] (secondary-fn (inc x)))]
(/ (another-fn x) 4)))
Even if you restrict yourself to single-character function names, you are in no danger of running out, as there are (about) 64 thousand Unicode characters, any one of which is a valid function name.
Given that you can in fact have names that are ten thousand characters long, you are on even safer ground.

How to check if a symbol is resolvable in the current namespace?

I want to check if a symbol is resolvable in the current namespace. What's the canonical way to do this?
After sifting through the API docs once more, I've stumbled on what might be the appropriate function:
; Returns the var or Class to which the symbol
; will be resolved in the current namespace, else nil.
(resolve 'foo)
; see also:
(ns-resolve *a-namespace* 'foo)
Take a look at this page. For example
(ns-map *ns*)
will give you a map of the bindings in the current namespace. You can examine this map to decide if your symbol is a key in the map,
(defn resolvable? [sym]
(contains? (ns-map *ns*) sym))
I do not know if this is the canonical way.