Clojure

different arrities of macro don't work in 1.2 (20100607 build)

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Resolution: Declined
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None

Description

I've been in the process of upgrading my framework from 1.1 to 1.2 and I've hit a serious stopper in code that works (and was tested) under 1.1.

~/clojure-workspace/cascade
$ lein repl
cascade.version=> (use 'cascade)
nil
cascade.version=> (link nil nil nil)
java.lang.IllegalArgumentException: Wrong number of args (3) passed to: cascade$link (NO_SOURCE_FILE:1)
cascade.version=> (doc link)
-------------------------
cascade/link
([env function] [env function extra-path-info] [env function extra-path-info query-parameters])
Macro
  Creates a link to a view or action function. Additional path info data may be specified (as a seq of
  data items), as well as query parameters (as a map whose keys are strings or keywords and whose values
  are converted to strings.). Uses standard keys from the env map. The resulting link is returned as a string.
nil
cascade.version=> (.printStackTrace *e)
java.lang.IllegalArgumentException: Wrong number of args (3) passed to: cascade$link (NO_SOURCE_FILE:1)
 at clojure.lang.Compiler.eval(Compiler.java:5437)
 at clojure.lang.Compiler.eval(Compiler.java:5388)
 at clojure.core$eval.invoke(core.clj:2370)
 at clojure.main$repl$read_eval_print__5620.invoke(main.clj:183)
 at clojure.main$repl$fn__5625.invoke(main.clj:203)
 at clojure.main$repl.doInvoke(main.clj:203)
 at clojure.lang.RestFn.invoke(RestFn.java:422)
 at user$eval9$fn__10.invoke(NO_SOURCE_FILE:1)
 at user$eval9.invoke(NO_SOURCE_FILE:1)
 at clojure.lang.Compiler.eval(Compiler.java:5421)
 at clojure.lang.Compiler.eval(Compiler.java:5412)
 at clojure.lang.Compiler.eval(Compiler.java:5412)
 at clojure.lang.Compiler.eval(Compiler.java:5388)
 at clojure.core$eval.invoke(core.clj:2370)
 at clojure.main$eval_opt.invoke(main.clj:234)
 at clojure.main$initialize.invoke(main.clj:253)
 at clojure.main$null_opt.invoke(main.clj:278)
 at clojure.main$main.doInvoke(main.clj:353)
 at clojure.lang.RestFn.invoke(RestFn.java:422)
 at clojure.lang.Var.invoke(Var.java:369)
 at clojure.lang.AFn.applyToHelper(AFn.java:165)
 at clojure.lang.Var.applyTo(Var.java:482)
 at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Wrong number of args (3) passed to: cascade$link
 at clojure.lang.AFn.throwArity(AFn.java:439)
 at clojure.lang.AFn.invoke(AFn.java:47)
 at cascade$link.invoke(cascade.clj:90)
 at cascade$link.invoke(cascade.clj:92)
 at clojure.lang.Var.invoke(Var.java:381)
 at clojure.lang.AFn.applyToHelper(AFn.java:180)
 at clojure.lang.Var.applyTo(Var.java:482)
 at clojure.lang.Compiler.macroexpand1(Compiler.java:5283)
 at clojure.lang.Compiler.macroexpand(Compiler.java:5338)
 at clojure.lang.Compiler.eval(Compiler.java:5406)
 ... 22 more
Reflection warning, NO_SOURCE_PATH:6 - reference to field printStackTrace can't be resolved.
nil
cascade.version=> 
</code></pre>
 

Here's the source:

<pre><code>
(defmacro link
  "Creates a link to a view or action function. Additional path info data may be specified (as a seq of
  data items), as well as query parameters (as a map whose keys are strings or keywords and whose values
  are converted to strings.). Uses standard keys from the env map. The resulting link is returned as a string."
  ([env function]
    (link env function nil))
  ([env function extra-path-info]
    (link env function extra-path-info nil))
  ([env function extra-path-info query-parameters]
    `(link-path ~env (link-map-from-function ~function ~extra-path-info ~query-parameters))))

So, (doc) shows the number of parameters correctly, as does the code, but attempting to use it with 3 parameters generates the spurious error.

Here's a simpler example:

<pre>
<code>
$ lein repl
cascade.version=> (defmacro ex ([a] (ex a "no-b")) ([a b] `(format "a=%s, b=%s" ~a ~b)))
#'cascade.version/ex
cascade.version=> (ex)
java.lang.IllegalArgumentException: Wrong number of args (2) passed to: version$ex (NO_SOURCE_FILE:1)
cascade.version=> (ex 1)
java.lang.IllegalArgumentException: Wrong number of args (2) passed to: version$ex (NO_SOURCE_FILE:1)
cascade.version=> (ex 1 2)
"a=1, b=2"
cascade.version=>
</code>
</pre>

Activity

Hide
Assembla Importer added a comment -
Show
Assembla Importer added a comment - Converted from http://www.assembla.com/spaces/clojure/tickets/383
Hide
Assembla Importer added a comment -

hlship said: Related association with ticket #384 was added

Show
Assembla Importer added a comment - hlship said: Related association with ticket #384 was added
Hide
Assembla Importer added a comment -

cgrand said: You were relying on an implementation detail (relying on macros being fns of same arity and those fns being not yet tagged as macro during the 1st evaluation of the body. Indeed this behaviour is brittle if your reload a new definition of the macro:

user=> (defmacro ex ([] (ex "v1")) ([v] (str "v1: I'm " v)))
#'user/ex
user=> (ex)
"v1: I'm v1"
user=> (defmacro ex ([] (ex "v2")) ([v] (str "v2: I'm " v)))
#'user/ex
user=> (ex)
"v1: I'm v2" ; and not "v2: I'm v2"</code></pre>

The correct way has always been to write:
<pre><code>(defmacro ex ([a] `(ex ~a "no-b")) ([a b] `(format "a=%s, b=%s" ~a ~b)))

and this till works with 1.2.

Show
Assembla Importer added a comment - cgrand said: You were relying on an implementation detail (relying on macros being fns of same arity and those fns being not yet tagged as macro during the 1st evaluation of the body. Indeed this behaviour is brittle if your reload a new definition of the macro:
user=> (defmacro ex ([] (ex "v1")) ([v] (str "v1: I'm " v)))
#'user/ex
user=> (ex)
"v1: I'm v1"
user=> (defmacro ex ([] (ex "v2")) ([v] (str "v2: I'm " v)))
#'user/ex
user=> (ex)
"v1: I'm v2" ; and not "v2: I'm v2"</code></pre>

The correct way has always been to write:
<pre><code>(defmacro ex ([a] `(ex ~a "no-b")) ([a b] `(format "a=%s, b=%s" ~a ~b)))
and this till works with 1.2.
Hide
Assembla Importer added a comment -

hlship said: Point taken; I've been reviewing Stu's book and I can see how he does as your example (what I've called the workaround), which is to have the short form expand into a macro call to the next larger form. Anyway, this does rub me a little oddly ... I had thought of macros as merely special functions whose execution was integrated into the Clojure reader/compiler but not otherwise unlike ordinary functions. This is clearly not the case, though I'm curious as to the underlying change from 1.1 to 1.2.

Further I think there's still an underlying problem:

user=> (defmacro ex ([a] `(ex ~a "no-b")) ([a b] `(format "a=%s, b=%s" ~a ~b)))
#'user/ex
user=> (ex)
java.lang.IllegalArgumentException: Wrong number of args (2) passed to: user$ex (NO_SOURCE_FILE:1)
user=> (ex 1)
"a=1, b=no-b"
user=> (ex 1 2)
"a=1, b=2"
user=> (ex 1 2 3)
java.lang.IllegalArgumentException: Wrong number of args (5) passed to: user$ex (NO_SOURCE_FILE:1)
user=>

Seems like macros now get two extra invisible arguments, presumably related to how they are now implemented. Even so, the error message is confusing since the programmer's code didn't pass 2 arguments, it passed 0 arguments.

Show
Assembla Importer added a comment - hlship said: Point taken; I've been reviewing Stu's book and I can see how he does as your example (what I've called the workaround), which is to have the short form expand into a macro call to the next larger form. Anyway, this does rub me a little oddly ... I had thought of macros as merely special functions whose execution was integrated into the Clojure reader/compiler but not otherwise unlike ordinary functions. This is clearly not the case, though I'm curious as to the underlying change from 1.1 to 1.2. Further I think there's still an underlying problem:
user=> (defmacro ex ([a] `(ex ~a "no-b")) ([a b] `(format "a=%s, b=%s" ~a ~b)))
#'user/ex
user=> (ex)
java.lang.IllegalArgumentException: Wrong number of args (2) passed to: user$ex (NO_SOURCE_FILE:1)
user=> (ex 1)
"a=1, b=no-b"
user=> (ex 1 2)
"a=1, b=2"
user=> (ex 1 2 3)
java.lang.IllegalArgumentException: Wrong number of args (5) passed to: user$ex (NO_SOURCE_FILE:1)
user=>
Seems like macros now get two extra invisible arguments, presumably related to how they are now implemented. Even so, the error message is confusing since the programmer's code didn't pass 2 arguments, it passed 0 arguments.
Hide
Assembla Importer added a comment -

cgrand said: The two implicit args are &env (a map whose keys are the locally bound symbols, values are unspecified) and &form (the whole form undergoing macroexpansion – it allows you to look at form's metadata). I reckon the off-by-two argcount in errors is annoying.

Show
Assembla Importer added a comment - cgrand said: The two implicit args are &env (a map whose keys are the locally bound symbols, values are unspecified) and &form (the whole form undergoing macroexpansion – it allows you to look at form's metadata). I reckon the off-by-two argcount in errors is annoying.
Hide
Assembla Importer added a comment -

hlship said: Right, so fixing that part of the problem, identifying the number of arguments in terms of what the coder wrote, rather than how Clojure is implemented, is a low priority fix, but one that should be addressed.

Show
Assembla Importer added a comment - hlship said: Right, so fixing that part of the problem, identifying the number of arguments in terms of what the coder wrote, rather than how Clojure is implemented, is a low priority fix, but one that should be addressed.
Hide
Assembla Importer added a comment -

stu said: Error reporting issue captured in #397.

Show
Assembla Importer added a comment - stu said: Error reporting issue captured in #397.

People

  • Assignee:
    Unassigned
    Reporter:
    Anonymous
Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: