Clojure

defn-created fns inherit old metadata from the Var they are assigned to

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None

Description

  • What (small set of) steps will reproduce the problem?

user> (def #^{:foo "bar"} x 5)
#'user/x
user> (meta #'x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1, :foo "bar"}
user> (defn x [] 5)
#'user/x
user> (meta #'x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1, :arglists ([])}
user> (meta x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1, :foo "bar"}

  • What is the expected output? What do you see instead?

I expect (meta #'x) to evaluate to the value of the final (meta x) in the above.

  • What version are you using?

Current master (commit 61202d2ff6925002400a9843e8fbd080f3bef3a5).

  • Was this discussed on the group? If so, please provide a link to the discussion.

http://groups.google.com/group/clojure/browse_thread/thread/4c7151aa9c4d919c/d1b033ef5a13dd89?lnk=gst&q=off-by-one#d1b033ef5a13dd89

Prompted by discussion at

http://groups.google.com/group/clojure/browse_thread/thread/6553d48c981019eb/3c55b0bd43a5d8e9?lnk=gst&q=off-by-one#3c55b0bd43a5d8e9

  • Initial attempt at a diagnosis:

I think this is due to DefExpr's eval method binding the Var to init.eval() first and attaching the supplied metadata to the Var later – see Compiler.java lines 341-352. (Note the Var is always already in place when init.eval() is called, regardless of whether it existed prior to the evaluation of the def / defn.) Thus the init expression supplied by defn sees the old (and wrong) metadata on the Var.

Activity

Hide
Assembla Importer added a comment -
Show
Assembla Importer added a comment - Converted from http://www.assembla.com/spaces/clojure/tickets/270 Attachments: dont-copy-val-metadata-onto-new-var-value-in-defn.patch - https://www.assembla.com/spaces/clojure/documents/dwK4yssayr37y_eJe5d-aX/download/dwK4yssayr37y_eJe5d-aX 0001-set-meta-on-vars-before-evaluating-their-init-see-27.patch - https://www.assembla.com/spaces/clojure/documents/arrhbiAI4r35lQeJe5cbLr/download/arrhbiAI4r35lQeJe5cbLr
Hide
Assembla Importer added a comment -
Show
Assembla Importer added a comment - stu said: [file:dwK4yssayr37y_eJe5d-aX]
Hide
Assembla Importer added a comment -

stu said: The problem happens with defn, but not with def+fn, so I think the original diagnosis is incorrect.

Another stab at diagnosis: The defn macro copies metadata from a var onto its new value, so if you defn a var that already exists, the old var metadata becomes metadata on the new value. I don't know why this would be the right thing to do, and if you eliminate this behavior (see patch) all the Clojure and Contrib tests still pass.

If this is correct, please assign back to me and I will write tests. If this is wrong, please tell me what's going on here so I know how to write the tests.

Show
Assembla Importer added a comment - stu said: The problem happens with defn, but not with def+fn, so I think the original diagnosis is incorrect. Another stab at diagnosis: The defn macro copies metadata from a var onto its new value, so if you defn a var that already exists, the old var metadata becomes metadata on the new value. I don't know why this would be the right thing to do, and if you eliminate this behavior (see patch) all the Clojure and Contrib tests still pass. If this is correct, please assign back to me and I will write tests. If this is wrong, please tell me what's going on here so I know how to write the tests.
Hide
Assembla Importer added a comment -

cgrand said: Child association with ticket #363 was added

Show
Assembla Importer added a comment - cgrand said: Child association with ticket #363 was added
Hide
Assembla Importer added a comment -

cgrand said: I think the original diagnosis is correct. Note that the behavior differ when def iscompiled:

user=> (def #^{:foo "bar"} x 5)
#'user/x
user=> (let [] (defn x [] 5))
#'user/x
user=> (meta x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_PATH", :line 83, :arglists ([])}
user=> (meta #'x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_PATH", :line 83, :arglists ([])}

See also http://groups.google.com/group/clojure/browse_thread/thread/6afd81896ca368b2#

Show
Assembla Importer added a comment - cgrand said: I think the original diagnosis is correct. Note that the behavior differ when def iscompiled: user=> (def #^{:foo "bar"} x 5) #'user/x user=> (let [] (defn x [] 5)) #'user/x user=> (meta x) {:ns #<Namespace user>, :name x, :file "NO_SOURCE_PATH", :line 83, :arglists ([])} user=> (meta #'x) {:ns #<Namespace user>, :name x, :file "NO_SOURCE_PATH", :line 83, :arglists ([])} See also http://groups.google.com/group/clojure/browse_thread/thread/6afd81896ca368b2#
Hide
Assembla Importer added a comment -
Show
Assembla Importer added a comment - cgrand said: [file:arrhbiAI4r35lQeJe5cbLr]
Hide
Assembla Importer added a comment -

cgrand said: My patch aligns DefExpr.eval with DefExpr.emit (first set meta then eval the init value).

Show
Assembla Importer added a comment - cgrand said: My patch aligns DefExpr.eval with DefExpr.emit (first set meta then eval the init value).
Hide
Assembla Importer added a comment -

cgrand said: Forget it, my current patch is broken.

Show
Assembla Importer added a comment - cgrand said: Forget it, my current patch is broken.
Hide
Assembla Importer added a comment -

cgrand said: My current patch is broken because of #352, what is the rational for #352?

Show
Assembla Importer added a comment - cgrand said: My current patch is broken because of #352, what is the rational for #352?
Alex Miller made changes -
Field Original Value New Value
Fix Version/s Approved Backlog [ 10034 ]
Fix Version/s Backlog [ 10035 ]
Alex Miller made changes -
Fix Version/s Backlog [ 10035 ]
Alex Miller made changes -
Priority Major [ 3 ]

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: