I have noticed that the defn
macro does not allow me to define a function with a qualified name. (defn foo/bar ...)
fails with an error such as:
Syntax error macroexpanding clojure.core/defn at (rte_construct.clj:401:1).
rte/expand - failed: simple-symbol? at: [:fn-name] spec: :clojure.core.specs.alpha/defn-args
However, I can use (def ...)
to achieve the same thing with no such error occurring.
I suspect that the error is erroneous. I.e., the syntax checker is overzealous.
If it is really important that defn
not be used to define functions with fully qualified names, shouldn't def
have the same restriction? And if using def
for this purpose is OK, then shouldn't the restriction be relaxed on defn
?
Why is the restriction in place for defn
?@jimka.issy You should use intern
for that.
defn
only allows you to define vars in the current namespace, so a qualified symbol is not appropriate
appropriate in what sense? would it break something in the language?
What do you mean about using intern
?
For example if i'm at the REPL, i might very well like to (defn ...)
any function in my project. Right?
defn
is just sugar over def
and the docstring of def
says:
> Creates and interns a global var with the name
> of symbol in the current namespace (ns) or locates such a var if
> it already exists.
If you want to intern a var in a different namespace, use intern
so how can I use intern to define a function in a different namespace without interning into the current one? sorry I don't understand your suggestion.
bar=> (intern 'foo 'foo-fn (fn []))
#'foo/foo-fn
OK, that's great. But it doesn't explain why defn has the extra syntax restriction that the name must be a simple symbol?
For example, I can define -defn
as follows
(defmacro -defn
"Like defn, but allows symbol naming the function to be in
any namespace, not just a simple symbol"
[name doc-string lambda-list & body]
`(def ~name ~doc-string (fn ~lambda-list ~@body)))
Is there anything dangerous about this, other than it doesnt handle the optional docstring as defn does, and doesnt handle var-args function defs?I'm not sure why defn
doesn't allow qualified symbols, but my guess would be that it's not very common to define things outside of the current namespace.
And for the uncommon case you can reach for intern
.
Or use in-ns
, etc.
I agree that it is probably the common case. However, I'd still like to know the reason for such a hostile syntax check?
If there is a real reason for the syntax check, then my use of -defn
may have the same problem. So I'd love to know about it.
The check is according to the docstring. Not letting slip through things that are against the docstring is helpful. You may disagree with the docstring, but that doesn't make it different.
Ahh the syntax check simply enforces the stated documentation.
So the question changes. Rather than asking about the syntax check, I need to ask about why the function is documented in such a restrictive way which doesn't match the implementation?
Michiel, I know you're not the one who did the implementation. so i'm not complaining in your direction.
I think your question is: why doesn't defn
let me define things outside the current namespace. That I don't know, other than my guess above.
@jimka.issy A good place to post your question might be http://ask.clojure.org/
This may also shed some light on it: https://stackoverflow.com/a/18141019/6264. def
is a special form, handled by the compiler
Which of the following is clearer?
(defn and ;; bdd/and
"Perform a Boolean AND on 0 or more Bdds."
([] true)
([bdd] bdd)
([bdd1 bdd2]
(cond
(= false bdd1) false
(= false bdd2) false
(= true bdd1) bdd2
(= true bdd2) bdd1
(identical? bdd1 bdd2) bdd1
:else (binary-op and bdd1 bdd2)))
([bdd1 bdd2 & bdds]
(reduce and (apply cons bdd1 bdd2 bdds))))
vs
(ns-defn bdd/and
"Perform a Boolean AND on 0 or more Bdds."
([] true)
([bdd] bdd)
([bdd1 bdd2]
(cond
(= false bdd1) false
(= false bdd2) false
(= true bdd1) bdd2
(= true bdd2) bdd1
(identical? bdd1 bdd2) bdd1
:else (binary-op and bdd1 bdd2)))
([bdd1 bdd2 & bdds]
(reduce and (apply cons bdd1 bdd2 bdds))))
I did not design Clojure, but I suspect that those who did find it perfectly clear that def
and defn
always and only are used to define Vars within the current namespace. The most common practice by far in Clojure is to have an ns
form near the beginning of each file, and then not to do in-ns
after that in source code files. (There are exceptions, but they are unusual). In this usual case, the namespace of every def
and defn
in the same file is very clearly understood.
Based on my experience with Clojure, I would personally find it confusing if there were def
and/or defn
forms that created Vars in namespaces other than the current one.
I can't say whether would be confusing or not. the the case is that def
allows it and defn
does not.
It seems that perhaps the implemention of def
allows it only if the namespace is the current namespace.
hmm.. yes i've only tried it with an alias to the current ns.
You asked which of two alternatives would be clearer. I tried to give reasons above why I think it is perfectly clear which namespace a def
or defn
is in, even if there is no namespace in the symbol given to def
or defn
-- because it is understood to be in the current namespace.
And for the majority of Clojure source files, every def
and defn
is in the same namespace, i.e. the one whose ns
form is near the beginning of the file.
I'm not a designer of Clojure, just a close observer, so do not treat my words with the weight of someone who has designed it.
OK, it's not a real hinderance for me. and in the end it's not really so important. just seems inconsistent to me, with no compelling reason. But with lisp, it's very easy to work around most syntactical problems, whether real or perceived
@jimka.issy def
doesn't allow you to define vars outside the current ns either
ahhh interesting. So that sounds like the syntax check is really wrong for defn. Because I've aliased the current ns to foo
and then attempted to (defn foo/my-fun ....)
which fails and (def foo/my-fn ...)
which succeeds. Looks like the syntax check for defn
should be changed to allow either a simple symbol, or a qualified symbol which resolves to the current namespace.
So that sounds like the syntax check is really wrong for defn
no
it's precisely how it's intended to be
the fact that def
allows the current namespace is a bit of a historical leftover
foo=> (ns bar)
nil
bar=> (def foo/bar 1)
Syntax error compiling def at (REPL:1:1).
Can't refer to qualified var that doesn't exist
bar=> (def bar/bar 1)
#'bar/bar
bar=> (ns foo)
nil
foo=> (def bar/bar 1)
Syntax error compiling def at (REPL:1:1).
Can't create defs outside of current ns