Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jafingerhut
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jafingerhut/5c9344fd4c20445eb64b to your computer and use it in GitHub Desktop.
Save jafingerhut/5c9344fd4c20445eb64b to your computer and use it in GitHub Desktop.
Clojure metadata lost during macro invocations
;; Clojure JIRA ticket CLJ-865: http://dev.clojure.org/jira/browse/CLJ-865
;; was created with the intent of changing some of the behavior below, but it is
;; not clear as of late 2014 whether the behavior is considered a bug that should
;; be changed.
;; Nicola Mometto pointed out that there may be similar issues with metadata
;; being eliminated for primInvoke's and functions defined with definline.
;; Perhaps http://dev.clojure.org/jira/browse/CLJ-1533 is related.
user=> *clojure-version*
{:major 1, :minor 6, :incremental 0, :qualifier nil}
user=> (import '[java.io Writer FileWriter])
java.io.FileWriter
user=> (require '[clojure.java.io :as io])
nil
user=> (defn m [form] ((juxt meta identity) (macroexpand form)))
#'user/m
;; All metadata eliminated from forms (ClassName. args)
user=> (m '(FileWriter. "a.txt"))
[nil (new FileWriter "a.txt")]
user=> (m '^{:foo 7 :tag Writer} (FileWriter. "a.txt"))
[nil (new FileWriter "a.txt")]
;; All metadata except :tag eliminated from forms (.instanceMethod args)
user=> (m '(.close (FileWriter. "a.txt")))
[nil (. (FileWriter. "a.txt") close)]
user=> (m '^{:foo 7 :tag Writer} (.close (FileWriter. "a.txt")))
[{:tag Writer} (. (FileWriter. "a.txt") close)]
;; All metadata except :tag eliminated from forms (Class/staticMethod args)
user=> (m '(Math/abs 5))
[nil (. Math abs 5)]
user=> (m '^{:foo 7 :tag Long} (Math/abs 5))
[{:tag Long} (. Math abs 5)]
;; All metadata preserved for forms (. ClassName instanceMethod args),
;; because no macroexpansion is occurring.
user=> (m '(. Math abs 5))
[{:line 1, :column 5} (. Math abs 5)]
user=> (m '^{:foo 7 :tag Long} (. Math abs 5))
[{:tag Long, :foo 7, :line 1, :column 5} (. Math abs 5)]
;; All metadata preserved for function calls. No macroexpansion is
;; occurring.
user=> (defn my-writer [& args] (apply io/writer args))
#'user/my-writer
user=> (m '(my-writer "a.txt"))
[{:line 1, :column 5} (my-writer "a.txt")]
user=> (m '^Writer (my-writer "a.txt"))
[{:tag Writer, :line 1, :column 5} (my-writer "a.txt")]
user=> (m '^{:foo 7 :tag Writer} (my-writer "a.txt"))
[{:tag Writer, :foo 7, :line 1, :column 5} (my-writer "a.txt")]
;; All metadata _including_ :tag eliminated for invocations of macros
;; defined with defmacro.
user=> (defmacro my-writer-macro [x] `(my-writer ~x))
#'user/my-writer-macro
user=> (m '(my-writer-macro "a.txt"))
[nil (user/my-writer "a.txt")]
user=> (m '^Writer (my-writer-macro "a.txt"))
[nil (user/my-writer "a.txt")]
user=> (m '^{:foo 7 :tag Writer} (my-writer-macro "a.txt"))
[nil (user/my-writer "a.txt")]
;; A let binding with a type hint is one way to avoid the reflection,
;; although a little more verbose.
user=> (set! *warn-on-reflection* true)
true
user=> (.close (my-writer-macro "a.txt"))
Reflection warning, /private/var/folders/v5/7hpqbpk51td3v351377gl6yw0000gn/T/form-init3862515282802286345.clj:1:1 - reference to field close can't be resolved.
nil
user=> (.close ^Writer (my-writer-macro "a.txt"))
Reflection warning, /private/var/folders/v5/7hpqbpk51td3v351377gl6yw0000gn/T/form-init3862515282802286345.clj:1:1 - reference to field close can't be resolved.
nil
user=> (let [^Writer w (my-writer-macro "a.txt")]
#_=> (.close w))
nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment