reader conditional not handling defmacro?

205 views
Skip to first unread message

hiskennyness

unread,
May 20, 2016, 1:34:43 PM5/20/16
to Clojure
My next problem with .cljc (which is going very very well overall!) has to do with macros.

Should I mention that I am building under Figwheel? This toolchain stuff has this old Lisper's head spinning.

Without a conditional, code using this macro compiles fine (the key bit being where I offer devs a little syntactic sugar in coding up defobserver so they can omit the dispatching types js/object  (which I myself rarely specify):

(defmacro defobserver [slot types params & body]
     (assert (keyword? slot) "defobserver> slot should be a keyword.")
     (let [ftypes (concat types (take-last (- 3 (count types))
                                           '(::tiltontec.modeller.cell-types/model
                                             js/object js/object)))
           fparams (concat params
                           (take-last (- 4 (count params))
                                      '(me new-value old-value c)))]
       `(defmethod tiltontec.modeller.observer/observe [~slot ~@ftypes][~'slot ~@fparams]
          ~@body)))

But on clj those need to be Object. So I tried this (after wrapping the above in #?(:cljs ...):

#_(:clj
   (defmacro defobserver [slot types params & body]
     (assert (keyword? slot) "defobserver> slot should be a keyword.")
     (let [ftypes (concat types (take-last (- 3 (count types))
                                           '(::tiltontec.modeller.cell-types/model
                                             Object Object)))
           fparams (concat params
                           (take-last (- 4 (count params))
                                      '(me new-value old-value c)))]
       `(defmethod tiltontec.modeller.observer/observe [~slot ~@ftypes][~'slot ~@fparams]
          ~@body))))

And in fact, that was a brave attempt to salvage my original attempt, which conditionalized just the types:

#?(:clj Object :cljs js/object)

I repeated that twice, but just realized this would have been a good spot for the splicing variant.

Anyway, no matter what I do I get a warning compiling to cljs that Object is unknown, and indeed a macroexpand-1 of '(defobserver :fu [nil][_ _ _ _] 42) shows Object where I should be seeing js/object.

I am also getting the same problem with an unrelated macro in which I am trying to conditionalize between dosync and do. Interestingly, I just noticed the same macro conditionally chooses between Exception. and js/Error. and I am not seeing warnings there!

Is this another known issue?

ps. I will try the old trick of having a helper defn with-defobserver and with-whatever to see if this is purely a macro interaction.




Sean Corfield

unread,
May 20, 2016, 2:15:09 PM5/20/16
to Clojure Mailing List

This is because the macros are expanded by Clojure so inside a macro, it always looks like :clj. Take a look at how Expectations gets around this:

 

https://github.com/jaycfields/expectations/blob/master/src/cljc/expectations/platform.cljc#L9-L21

 

I’d love to hear if there’s an “official” recommended way to deal with reader conditionals in macros used from ClojureScript?

 

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

hiskennyness

unread,
May 20, 2016, 2:16:47 PM5/20/16
to Clojure


On Friday, May 20, 2016 at 1:34:43 PM UTC-4, hiskennyness wrote:
My next problem with .cljc (which is going very very well overall!) has to do with macros.
....snip

ps. I will try the old trick of having a helper defn with-defobserver and with-whatever to see if this is purely a macro interaction.

Not surprisingly, that worked on the second (unposted) example which could be divided/conquered into a helper defn (tho my CL is getting rusty, the old trick is  call-with-whatever).

Unfortunately the other case (the one shown) really is a conditional code-rewriting that has to happen at macroexpansion time, so a fix is still needed.


hiskennyness

unread,
May 20, 2016, 2:22:10 PM5/20/16
to Clojure
Thanks, Sean. (My follow-up fix request went out just as your fix came in.)

-kt

Dan Burton

unread,
May 20, 2016, 5:23:28 PM5/20/16
to clo...@googlegroups.com
What about something like

;; obj.cljc
(ns obj)

(def obj #?(:clj Object :cljs js/Object))

(defmacro get-obj []
  `obj)

;; obj-test.cljc
(ns obj-test
  (:require [obj :refer-macros [get-obj]]))

(def gotten-obj (obj/get-obj))

Load obj-test in clj, and obj-test/gotten-obj is Object. Load it in cljs, and gotten-obj is js/Object. Thus, you can write cljc macros that symbolically refer to obj/obj and turn out as the correct thing. This is because the macro doesn't refer to the *value* of obj, just to the symbol, which isn't evaluated until runtime.

-- Dan Burton

--
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.
For more options, visit https://groups.google.com/d/optout.

Herwig Hochleitner

unread,
May 22, 2016, 6:57:14 PM5/22/16
to clo...@googlegroups.com
This thread just came up, while I was in the process of composing a mail on this topic to clojure-dev: https://groups.google.com/d/msg/clojure-dev/f6ULUVokXrU/3uue5okSAgAJ

2016-05-20 23:22 GMT+02:00 Dan Burton <danburt...@gmail.com>:
What about something like


(def obj #?(:clj Object :cljs js/Object))

Unfortunately, doesn't work for cases, where you need to generate different syntax, eg `(try .. (catch Throwable e ...))` vs `(try ... (catch :default e ...))`.

Nathan Davis

unread,
May 27, 2016, 12:47:09 AM5/27/16
to Clojure
I can't reply to the thread on the Dev list, but here's my take.

Unless I'm missing something, it seems to me that most these issues could be resolved by having a dynamic var that gets set during macro expansion according to the target platform.  For instance, *compiler-target* is set to :clj or :cljs, depending on whether we're compiler for the JVM or Javascript.  If we want to be a little more sophisticated, *compiler-target* could be a set whose members are the same keywords that were "active" at read-time.  That would have the additional convenience that *compiler-target* would act as a predicate, indicating whether a particular "feature" is available for the target platform.

Then if a macro needs to be expanded in a certain way for certain platforms, it can just consult *compiler-target*.

Anyway, that's my 2-cents.

Nathan Davis

Alex Miller

unread,
May 27, 2016, 8:39:15 AM5/27/16
to Clojure
That is one option and is in the ballpark of http://dev.clojure.org/jira/browse/CLJ-1750.

Nathan Davis

unread,
May 27, 2016, 11:33:38 AM5/27/16
to Clojure
Alex,

On the Dev list, you mentioned you had discussed this several times with Rich and others, but were unable to reach concensus.  May I ask what the hangup is (i.e., what reservations / objections were expressed) and what other options came out of those discussions?

Nathan Davis
Reply all
Reply to author
Forward
0 new messages