tools.macro

Handling of namespaced symbols

Details

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

Description

Here is a snippet:

(def spec-map
  {:int {:type 'ints}})

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
         (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
           form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "my type" type$)
     (caster x)
     (aget x 0))))

It's broken, the error is "Unable to resolve symbol: type$ in this context". Why is it so? Let's macroexpand it:

(clojure.tools.macro/symbol-macrolet 
    [clojure.core.matrix.impl.ndarray-magic/type$ ints] 
    (def test-getter 
      (fn* ([x] (let* [x x] (prn "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))

Now, the reason is obvious: symbol-macrolet expects namespaced symbol, and in expanded form "type$" is once namespaced and once not. I think that symbol-macrolet can (should?) ignore namespacing here.

Activity

Dmitry Groshev made changes -
Field Original Value New Value
Description Here is a snippet:

{code}
(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
         (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
           form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "my type" type$)
     (caster x)
     (aget x 0))))
{code}

It's broken, the error is "Unable to resolve symbol: type$ in this context". Why is it so? Let's macroexpand it:

{code}
(clojure.tools.macro/symbol-macrolet
    [clojure.core.matrix.impl.ndarray-magic/type$ ints]
    (def test-getter
      (fn* ([x] (let* [x x] (prn "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))
{code}

Now, the reason is obvious: symbol-macrolet expects namespaced symbol, and in expanded form "type$" is once namespaced and once not. I that that symbol-macrolet can (should?) ignore namespacing here.
Here is a snippet:

{code}
(def spec-map
  {:int {:type 'ints}})

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
         (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
           form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "my type" type$)
     (caster x)
     (aget x 0))))
{code}

It's broken, the error is "Unable to resolve symbol: type$ in this context". Why is it so? Let's macroexpand it:

{code}
(clojure.tools.macro/symbol-macrolet
    [clojure.core.matrix.impl.ndarray-magic/type$ ints]
    (def test-getter
      (fn* ([x] (let* [x x] (prn "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))
{code}

Now, the reason is obvious: symbol-macrolet expects namespaced symbol, and in expanded form "type$" is once namespaced and once not. I that that symbol-macrolet can (should?) ignore namespacing here.
Dmitry Groshev made changes -
Description Here is a snippet:

{code}
(def spec-map
  {:int {:type 'ints}})

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
         (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
           form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "my type" type$)
     (caster x)
     (aget x 0))))
{code}

It's broken, the error is "Unable to resolve symbol: type$ in this context". Why is it so? Let's macroexpand it:

{code}
(clojure.tools.macro/symbol-macrolet
    [clojure.core.matrix.impl.ndarray-magic/type$ ints]
    (def test-getter
      (fn* ([x] (let* [x x] (prn "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))
{code}

Now, the reason is obvious: symbol-macrolet expects namespaced symbol, and in expanded form "type$" is once namespaced and once not. I that that symbol-macrolet can (should?) ignore namespacing here.
Here is a snippet:

{code}
(def spec-map
  {:int {:type 'ints}})

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
         (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
           form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "my type" type$)
     (caster x)
     (aget x 0))))
{code}

It's broken, the error is "Unable to resolve symbol: type$ in this context". Why is it so? Let's macroexpand it:

{code}
(clojure.tools.macro/symbol-macrolet
    [clojure.core.matrix.impl.ndarray-magic/type$ ints]
    (def test-getter
      (fn* ([x] (let* [x x] (prn "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))
{code}

Now, the reason is obvious: symbol-macrolet expects namespaced symbol, and in expanded form "type$" is once namespaced and once not. I think that symbol-macrolet can (should?) ignore namespacing here.
Hide
Konrad Hinsen added a comment - - edited

After a bit of thought, I think the right solution is to treat symbol macros like ordinary symbols in evaluation. This means that global definitions (defsymbolmacro) use namespaced symbols, whereas local symbol definitions (symbol-macrolet) accept only plain symbols and raise an exception for qualified symbols, just like let does.

With those rules, the way tools.macro handles namespaces in correct code is correct, but it doesn't do the required error handling because it lets you symbol-macrolet qualified symbols.

Note also that according to those rules, your example code is not correct. You have to write

(symbol-macrolet [~'type$ ...] ...)

and

(defmacro caster [x]
  `(~'type$ ~x))

although you might then prefer to write the latter as

(defmacro caster [x]
   (list 'type$ x))

but that's a matter of taste.

Show
Konrad Hinsen added a comment - - edited After a bit of thought, I think the right solution is to treat symbol macros like ordinary symbols in evaluation. This means that global definitions (defsymbolmacro) use namespaced symbols, whereas local symbol definitions (symbol-macrolet) accept only plain symbols and raise an exception for qualified symbols, just like let does. With those rules, the way tools.macro handles namespaces in correct code is correct, but it doesn't do the required error handling because it lets you symbol-macrolet qualified symbols. Note also that according to those rules, your example code is not correct. You have to write
(symbol-macrolet [~'type$ ...] ...)
and
(defmacro caster [x]
  `(~'type$ ~x))
although you might then prefer to write the latter as
(defmacro caster [x]
   (list 'type$ x))
but that's a matter of taste.

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: