clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
Jim Newton 2020-11-07T12:44:20.165400Z

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 ?

borkdude 2020-11-07T12:45:24.165900Z

@jimka.issy You should use intern for that.

borkdude 2020-11-07T12:45:42.166300Z

defn only allows you to define vars in the current namespace, so a qualified symbol is not appropriate

Jim Newton 2020-11-07T12:47:15.167600Z

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?

borkdude 2020-11-07T12:48:22.168300Z

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.

borkdude 2020-11-07T12:48:42.168700Z

If you want to intern a var in a different namespace, use intern

Jim Newton 2020-11-07T12:49:49.169200Z

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.

borkdude 2020-11-07T12:52:04.169500Z

bar=> (intern 'foo 'foo-fn  (fn []))
#'foo/foo-fn

Jim Newton 2020-11-07T12:59:15.170300Z

OK, that's great. But it doesn't explain why defn has the extra syntax restriction that the name must be a simple symbol?

Jim Newton 2020-11-07T13:00:17.171300Z

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?

borkdude 2020-11-07T13:01:51.171900Z

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.

borkdude 2020-11-07T13:02:16.172200Z

And for the uncommon case you can reach for intern.

borkdude 2020-11-07T13:02:30.172400Z

Or use in-ns, etc.

Jim Newton 2020-11-07T13:03:34.173200Z

I agree that it is probably the common case. However, I'd still like to know the reason for such a hostile syntax check?

Jim Newton 2020-11-07T13:04:11.174200Z

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.

borkdude 2020-11-07T13:04:51.175100Z

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.

Jim Newton 2020-11-07T13:05:29.175600Z

Ahh the syntax check simply enforces the stated documentation.

Jim Newton 2020-11-07T13:06:16.176300Z

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?

Jim Newton 2020-11-07T13:07:13.177600Z

Michiel, I know you're not the one who did the implementation. so i'm not complaining in your direction.

borkdude 2020-11-07T13:07:14.177700Z

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.

borkdude 2020-11-07T13:10:51.178300Z

@jimka.issy A good place to post your question might be http://ask.clojure.org/

borkdude 2020-11-07T13:12:09.178600Z

This may also shed some light on it: https://stackoverflow.com/a/18141019/6264. def is a special form, handled by the compiler

Jim Newton 2020-11-07T13:44:56.179300Z

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))))

2020-11-07T13:52:13.179400Z

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.

2020-11-07T13:54:05.179600Z

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.

Jim Newton 2020-11-07T14:03:25.179800Z

I can't say whether would be confusing or not. the the case is that def allows it and defn does not.

2020-11-07T14:08:35.180300Z

It seems that perhaps the implemention of def allows it only if the namespace is the current namespace.

Jim Newton 2020-11-07T14:11:09.180500Z

hmm.. yes i've only tried it with an alias to the current ns.

2020-11-07T14:11:15.180700Z

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.

2020-11-07T14:12:22.180900Z

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.

2020-11-07T14:13:10.181100Z

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.

Jim Newton 2020-11-07T14:13:45.181300Z

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

bronsa 2020-11-07T15:11:42.182100Z

@jimka.issy def doesn't allow you to define vars outside the current ns either

1
Jim Newton 2020-11-08T15:41:33.182700Z

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.

bronsa 2020-11-10T14:01:07.188700Z

So that sounds like the syntax check is really wrong for defn

bronsa 2020-11-10T14:01:08.188900Z

no

bronsa 2020-11-10T14:01:15.189100Z

it's precisely how it's intended to be

bronsa 2020-11-10T14:01:42.189500Z

the fact that def allows the current namespace is a bit of a historical leftover

bronsa 2020-11-07T15:12:39.182500Z

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