Can't use my macro in fuction definition

134 views
Skip to first unread message

ru

unread,
Oct 19, 2019, 6:32:25 AM10/19/19
to Clojure
Dear Clojure users and team!

I have defined a macros to get an instance of arbitrary Java class and tried to test it:

ru@ru-iMacMint:~$ lein repl
nREPL server started on port 46225 on host 127.0.0.1 - nrepl://127.0.0.1:46225
WARNING: cat already refers to: #'clojure.core/cat in namespace: net.cgrand.regex, being replaced by: #'net.cgrand.regex/cat
REPL-y 0.3.7, nREPL 
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 11.0.2+9-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (defmacro instance [x & args] `(new ~x ~@args))
#'user/instance
user=> (instance javax.swing.JLabel "Ru")
#object[javax.swing.JLabel 0x53754b7 "javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=Ru,verticalAlignment=CENTER,verticalTextPosition=CENTER]"]
user=> (defn test [x] (let [i (instance x)] i))
WARNING: test already refers to: #'clojure.core/test in namespace: user, being replaced by: #'user/test

CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: x, compiling:(null:1:24) 
user=> (defn itest [x] (let [i (instance x)] i))

CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: x, compiling:(null:1:25) 
user=> 


Why I can't use my macro in a function definition?

Any help would be greatly appreciated.

Sincerely,

     Ru.


Matching Socks

unread,
Oct 19, 2019, 7:09:01 AM10/19/19
to Clojure
The macro is a code generator, with which the compiler computes the actual definition of the "test" function.  What's there is x, and the macro does not like x.  The REPL definition of JLabel worked because JLabel was the literal argument.  See https://clojure.org/reference/macros, which is subtle about this point, or the excellent book "Mastering Clojure Macros". 

ru

unread,
Oct 19, 2019, 10:02:51 AM10/19/19
to Clojure
Ok,  Matching Socks.

On what name should I replace the variable name "x" in the function definition so that the macro like it?

суббота, 19 октября 2019 г., 14:09:01 UTC+3 пользователь Matching Socks написал:

Chris Nuernberger

unread,
Oct 19, 2019, 10:15:23 AM10/19/19
to clo...@googlegroups.com
Hey Ru,

Renaming x to anything will result in roughly the same error in your function.  The problem is that your instance macro needs to know the classname at compile time.  As x is a runtime variable, the compiler cannot see the literal value of x at compile time.  Put another way, in your test function, with no other changes, you need a switch statement that tests if x is a known type and then calls the macro with the now-known-at-compile-time literal classname.


An instance function that found the constructor of x via reflection and then called that is what you want here.


Chris

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/4bdd8dee-0b87-42a1-8d3a-4a2cf60f3aff%40googlegroups.com.

ru

unread,
Oct 19, 2019, 10:28:33 AM10/19/19
to Clojure


суббота, 19 октября 2019 г., 17:15:23 UTC+3 пользователь Chris Nuernberger написал:
Hey Ru,

Renaming x to anything will result in roughly the same error in your function.  The problem is that your instance macro needs to know the classname at compile time.  As x is a runtime variable, the compiler cannot see the literal value of x at compile time.  Put another way, in your test function, with no other changes, you need a switch statement that tests if x is a known type and then calls the macro with the now-known-at-compile-time literal classname.


An instance function that found the constructor of x via reflection and then called that is what you want here.

May be this reflection should do "new" macro? 

Chris

On Sat, Oct 19, 2019 at 8:03 AM ru <sor...@oogis.ru> wrote:
Ok,  Matching Socks.

On what name should I replace the variable name "x" in the function definition so that the macro like it?

суббота, 19 октября 2019 г., 14:09:01 UTC+3 пользователь Matching Socks написал:
The macro is a code generator, with which the compiler computes the actual definition of the "test" function.  What's there is x, and the macro does not like x.  The REPL definition of JLabel worked because JLabel was the literal argument.  See https://clojure.org/reference/macros, which is subtle about this point, or the excellent book "Mastering Clojure Macros". 

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clo...@googlegroups.com.

Matching Socks

unread,
Oct 19, 2019, 12:12:46 PM10/19/19
to Clojure
Macros manipulate program symbols.  Macros fill the role that is filled by Perl scripts in the Java world, when they pre-process stuff (a database schema, or a gui model, for example) into actual Java code before you compile it.  If a task could not be solved by pre-processing the source code before the program starts to run, then it is not a task for a macro.  

On the bright side, using Java reflection is less tedious in Clojure than it is in Java.

ri...@chartbeat.com

unread,
Oct 19, 2019, 3:02:44 PM10/19/19
to clo...@googlegroups.com
Macros also allow you to create a dsl to make your code easier to use or understand by obfuscating away bits. For example a macro that times a function call by proxying the call or wrapping a call to defn.

That said, I consider many uses of macros that I’ve seen to be antipatterns that lead to hard to understand code. They should almost always be considered a last resort. 


On Oct 19, 2019, at 12:12 PM, Matching Socks <phill...@gmail.com> wrote:

Macros manipulate program symbols.  Macros fill the role that is filled by Perl scripts in the Java world, when they pre-process stuff (a database schema, or a gui model, for example) into actual Java code before you compile it.  If a task could not be solved by pre-processing the source code before the program starts to run, then it is not a task for a macro.  

On the bright side, using Java reflection is less tedious in Clojure than it is in Java.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/2c56ec62-9908-40b8-aacc-aecaf157049c%40googlegroups.com.

Marc Limotte

unread,
Oct 20, 2019, 8:35:48 AM10/20/19
to clo...@googlegroups.com
Hey Ru,

I'd also add that `new` is a special form, not a function.  It is evaluated at compile-time, which is why the macro doesn't work.  You can also try macro-expansion to see what's going on:

(defn itest [x] (macroexpand-1 '(instance x)))
=> #'user/itest
(itest java.lang.String)
=> (new x)

So, after macro-expansion, this would be like typing the following code:

(defn itest [x] (new x))
Syntax error (IllegalArgumentException) compiling new at [...]
Unable to resolve classname: x

This doesn't work as a straight up function, so it won't work as a macro. Macros are "shortcuts".  They won't let you do anything you couldn't do with "regular" code.
 
Finally, as Chris suggested above, reflection is probably what you want.  Here's a sample to get you started:

(defn itest [x & args]
  (let [cnstr (.getConstructor x (into-array java.lang.Class [String]))]
    (.newInstance cnstr (into-array java.lang.Object args))))
=> #'user/itest
(itest java.lang.String "Hello")
=> "Hello"
 
This is only a start though, because as written (line 2 in particular), it only works for constructors that take exactly one String argument.  To do this for real, you would need to build the [String] array dynamically by introspecting the args list.

Marc




ru

unread,
Oct 20, 2019, 1:59:48 PM10/20/19
to Clojure
Thank you, Marc!

I will try to use your example to develop more general "instance" function.

Sincerely,
  Ru
воскресенье, 20 октября 2019 г., 15:35:48 UTC+3 пользователь mlimotte написал:

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clo...@googlegroups.com.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clo...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages