Clojure defmacro loses metadata

前端 未结 2 729
醉酒成梦
醉酒成梦 2020-12-05 07:29

I am trying to create a little Clojure macro that defs a String with a type hint:

(defmacro def-string [name value]
  `(def ^String ~name ~value         


        
相关标签:
2条回答
  • 2020-12-05 07:52

    Metadata doesn't show up in a macroexpand since it's supposed to be "invisible".

    If the macro is correct (which it isn't) you should be able to call (meta #'db-host-option) to inspect the meta data on the var.

    Note that (def sym ...) inserts metadata on the var that it receives from the symbol. But ^Tag ~name sets the meta data on ~name (unquote name), not on the passed in symbol bound to name. It can't do anything else since ^Tag ... processing is done by the reader, which is already finished once macro expansion starts.

    You want something like

    (defmacro def-string [name value]
      `(def ~(with-meta name {:tag String}) ~value))
    
    
    user> (def-string bar 1)
    #'user/bar
    user> (meta #'bar)
    {:ns #<Namespace user>, :name bar, :file "NO_SOURCE_FILE", :line 1, :tag java.lang.String}
    
    0 讨论(0)
  • 2020-12-05 07:57

    ^ is a reader macro. defmacro never gets to see it. The hint is put on the list (unquote name). Compare for example (meta ^String 'x) with (meta ' ^String x) to see the effect.

    You need to put the hint on the symbol.

    (defmacro def-string
      [name value]
      `(def ~(vary-meta name assoc :tag `String) ~value))
    

    And the usage:

    user=> (def-string foo "bar")
    #'user/foo
    user=> (meta #'foo)
    {:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 5, :tag java.lang.String}
    
    0 讨论(0)
提交回复
热议问题