<< Back to previous view

[CLJ-383] different arrities of macro don't work in 1.2 (20100607 build) Created: 18/Jun/10  Updated: 24/Aug/10  Resolved: 24/Aug/10

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect
Reporter: Anonymous Assignee: Unassigned
Resolution: Declined Votes: 0
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>



 Comments   
Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

Converted from http://www.assembla.com/spaces/clojure/tickets/383

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

hlship said: Related association with ticket #384 was added

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

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.

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

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.

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

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.

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

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.

Comment by Assembla Importer [ 24/Aug/10 8:47 AM ]

stu said: Error reporting issue captured in #397.

Generated at Mon Sep 22 19:22:58 CDT 2014 using JIRA 4.4#649-r158309.