Last active
August 29, 2015 14:09
-
-
Save jafingerhut/5c9344fd4c20445eb64b to your computer and use it in GitHub Desktop.
Clojure metadata lost during macro invocations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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