Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

The following code fails (both in 1.6 and latest 1.7-alpha4):

`
user=> (ns foo)
nil
foo=> (def inc inc)
WARNING: inc already refers to: #'clojure.core/inc in namespace: foo, being replaced by: #'foo/inc

'foo/inc

;; Note inc is unbound at this point, which causes the exception below
foo=> inc

foo=> (ns bar)
nil
bar=> (require ['foo :refer ['inc]])
WARNING: inc already refers to: #'clojure.core/inc in namespace: bar, being replaced by: #'foo/inc
nil
bar=> (inc 8)

IllegalStateException Attempting to call unbound fn: #'foo/inc clojure.lang.Var$Unbound.throwArity (Var.java:43)
`

Further investigation shows that foo/inc is unbound:

foo/inc
=> #

Further investigation also shows that replacing the (def inc inc) with almost anything else, e.g. (def inc dec), (def inc clojure.core/inc), or (def inc (fn (link: n) (+ n 1))), causes no exception (but the warnings remain).

I would expect:
a) foo/inc should be bound and have the same value as clojure.core/inc
b) No error when requiring foo/inc
c) bar/inc should be bound to foo/inc

22 Answers

0 votes
by

Comment made by: bronsa

The second error should be expected, the right syntax should be (require (link: 'foo :refer ['inc)]) (note the leading quote before inc)

0 votes
by

Comment made by: mikera

Thanks for the catch Nicola - I've edited the description. Still get the same error however (just with a slightly different message)

0 votes
by

Comment made by: alexmiller

See comment...

0 votes
by

Comment made by: mikera

@Alex what comment? Note that the error still occurs even with the right syntax....

0 votes
by

Comment made by: mikera

Appears to have been closed prematurely

0 votes
by

Comment made by: alexmiller

I can't reproduce with the correct syntax:

`
Clojure 1.7.0-master-SNAPSHOT
user=> (ns foo)
nil
foo=> (def inc inc)
WARNING: inc already refers to: #'clojure.core/inc in namespace: foo, being replaced by: #'foo/inc

'foo/inc

foo=> (ns bar)
nil
bar=> (require ['foo :refer ['inc]])
WARNING: inc already refers to: #'clojure.core/inc in namespace: bar, being replaced by: #'foo/inc
nil
`

0 votes
by

Comment made by: mikera

The problem is that the var is still unbound and causes e.g. the following error:

=> (foo/inc 8)
IllegalStateException Attempting to call unbound fn: #'foo/inc clojure.lang.Var$Unbound.throwArity (Var.java:43)

I don't think that should be expected - or am I missing something?

0 votes
by

Comment made by: alexmiller

Ah, will take a look. But not right now. :)

0 votes
by

Comment made by: jafingerhut

Updated the description with a few more details. The exception goes away if you do (def inc (fn (link: n) (+ n 1))) instead of (def inc inc), for example. The warnings remain.

0 votes
by

Comment made by: tcrayford

Unsure if this is the same issue (I think it might be?), but I reproduced the exact same error message with AOT compilation involved:

reproduced in this git repository: https://github.com/yeller/compiler_update_not_referenced_bug

clone it, run lein do clean, uberjar, test, and that error message will show up every time for me

0 votes
by

Comment made by: jafingerhut

Mike, I think replacing (def inc inc) in your example with (def inc clojure.core/inc) should be considered as a reasonable workaround for this issue, unless you have some use case where you need to def inc to something that is not in clojure.core (and if so, why?)

The reason (def inc inc) behaves this way is, if not absolutely necessary, at least commonly used in Clojure programs to define recursive functions, e.g. (defn fib (link: n) (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2))))), so that the occurrences of fib in the body are resolved to the fib being defined.

0 votes
by

Comment made by: alexmiller

Moving to 1.7 until I can look at this more deeply.

0 votes
by

Comment made by: mikera

Andy - yes the workaround is fine for me right now.

I don't think this is an urgent issue but it may be exposing a subtle complexity regarding assumptions about the state of the namespace at different times. Perhaps the semantics should be something like:
- The def statement itself should be run before the var is interned. e.g. (def inc (inc 5)) should result in (def inc 6)
- Anything complied / deferred to run after completion of the def statement should use the new var (i.e. the new var should be referenced by fns, lazy sequences etc.)

0 votes
by

Comment made by: jafingerhut

I'm not sure what your proposal means in a case like this:

(def inc (fn [x] (inc x)))

Is the second inc to be interpreted/resolved before or after the new inc is created? Because it is (fn ...) it should be the after-behavior? What else besides fn should cause the after-behavior, rather than the before-behavior?

Even more fun (not saying that people often write code like this, but the compiler can handle it today):

`
(def inc (if (> (inc y) 5)

       (fn [x] (inc x))
       (fn [x] (dec x))))

`

I think the current compiler behavior of 'in the body of a def, the def'd symbol always refers to the new var, not any earlier def'd vars' is fairly straightforward to explain.

0 votes
by

Comment made by: tcrayford

Should I file the AOT issue reproduced in that thing as a new issue?

...