(list
(declare ^:dynamic p)
(defn q [] @p))
(binding [p (atom 10)]
(q))
> java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.IDeref
Thanks to @cemerick for helping me condense the snippet, and thanks to both @cemerick and @chouser for the lively discussion on IRC. Yet the discussion was inconclusive. Is the above expected behavior?
Micah
I'm not sure. Maybe it's undefined? Clojure compiles per top-level form. In this case this is the call to list. However in p does not exist before. It is created by the def inside this form. And maybe I'm just a weenie, but I wouldn't bet on some not very well documented peculiarities of def.
From the “one top-level form” rule do is an exception. And indeed this seems to be for a reason:
user=> (do (declare ^:dynamic p) (defn q [] @p))
#'user/q
user=> (binding [p (atom 10)] (q))
10
So maybe the rule of thumb is: never reference defs made in the same top-level form unless this form is a do.
I'm sorry. I can't explain things further. I suspect that the effect you see is due to some accidental, implementation-dependent behavior of the compiler.
Sincerely
Meikel
But it only interns the Var, it doesn't fully set it up. Particularly
relevant to the OP's example is that the metadata from the name symbol
is not transferred to the Var (and the changes to the Var based on
:dynamic are not applied) until runtime for the 'def', even though the
Var exists at compile time.
Here's a macro that expands at compile time to the *compile* time
metadata of the var named in its argument:
(defmacro compile-time-meta [x] (meta (resolve x)))
Now observe how it behaves differently than a runtime call to 'meta':
(vector
(declare ^:dynamic *myvar*)
(meta #'*myvar*)
(compile-time-meta #'*myvar*)))
The above returns:
[#'user/*myvar*
{:ns #<Namespace user>, :name *myvar*, :dynamic true, :declared true, ...}
{:ns #<Namespace user>, :name #<Unbound Unbound: #'user/*myvar*>}]
First is the Var itself.
Next is the metadata of the Var at runtime, after the entire form has
been compiled and therefore the metadata from the name has been
applied to the Var, including the :dynamic flag.
Finally we see that when our macro was expanded the Var existed but
had minimal metadata. This was after the declare was compiled but
before any part of the 'vector' form was run. There is no :dynamic
flag, and anything that depends on that flag at compile time to work
correctly (such as a function that refers the for Var) will fail to
work correctly.
--Chouser
Just one more question: It is defined behavior, or should I submit a patch for Clojure 1.3?
Micah
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
What exactly would the patch do?
Stu
>> Just one more question: Is it defined behavior, or should I submit a patch for Clojure 1.3?
>>
>> Micah
>
> What exactly would the patch do?
The patch would pass the following test:
(list
(declare ^:dynamic p)
(defn q [] @p))
(should= 10 (binding [p (atom 10)] (q)))
Implementation: I have little experience with the Clojure code base, but I would attempt the following approaches:
* fully evaluate def/declare forms as they are read, similar to the way root level (and do) forms are evaluated immediately.
* figure out what changed between versions 1.2 and 1.3 that caused the behavior difference
* git-bisect the Clojure repo on the above test case ???
Stu, based on your question, I'm sensing that the behavior is in the "undefined" realm…. thoughts?
Micah
Rich
(describe "something"
(context "fooey"
(with bar 42) ; declare comes from this macro
(it "works"
(should= 42 @bar)))) ; here bar be unbound
I'll see if I can restructure Speclj to get all the Vars declared at the root level. But I'd still like to know….
Is this defined behavior? Or am I jumping through hoops to avoid behavior that doesn't belong?
Micah
Hi Micah,
maybe this can help : do you know you can create / use vars without
having to have them declared globally ?
See section named "non interned vars" here : http://clojure.org/vars
HTH,
-- Laurent