cljc - enabled macros

219 views
Skip to first unread message

Herwig Hochleitner

unread,
May 22, 2016, 6:51:02 PM5/22/16
to cloju...@googlegroups.com
With .cljc and reader conditionals, the story for writing platform - spanning clojure has become pretty good, except for one blatantly missing piece: We don't have macros that can generate different code for different targets.
Reader tags don't help there, since they always expand for the environment the compiler runs in.
When dealing with macros in cljc, this problem comes up frequently, due to the practice of putting otherwise compatible namespaces under different names.

Right now, I identify three possible solutions an application developer can choose from:

1. perpetuate the namespace split, as seen in the core.* contrib macro namespaces
    (that's not ideal for programs that commit to platform-independence)
2. push all the platform-dependency out into cljc functions
    (that's not always feasible, e.g. when emitting go-blocks)
3. look at the &env implicit argument to dispatch for your target runtime
    (that's relying on an obscure implementation detail)

To illustrate how I could see this working, I have defined a `defmacro*` macro, that looks at its &env and provides a platform dispatch, similar to reader conditionals [1]. This seems to work well for defining platform-spanning macros [2].

It's still not optimal, as one has to emit fully expanded namespaces for cljs because syntax-quote will still expand for clj, but at least this makes it possible to write such macros.

While composing this email, somebody posted a similar topic on the user mailing list [3], so the need for such facilities definitely is there.

Any thoughts on how this could be supported properly?



Alex Miller

unread,
May 22, 2016, 8:02:13 PM5/22/16
to cloju...@googlegroups.com
I've discussed this several times with Rich, David N, Luke, Tim B, and others but we have not (yet) ever reached a consensus of what, if anything, should be done. To be clear, the issue here is that macros are always implemented in Clojure (for both platforms) and macros *produce* code so having reader conditionals don't help you - you want conditional *emitting*.

I'm not sure I'd say the three points below are exactly the way I've seen people handling this although maybe I'm just using a slightly different choice of words/details. In particular, one option is to keep the *macros* (but not the rest of the code) split across platforms (and include the right one in a cljc via reader conditionals), which is maybe somewhere between 1 and 2. I realize this is easier in some libs/apps than in others. For #3, I've seen several other ways to detect the correct target platform, but they're all tricks of a sort.

There is a ticket in this ballpark at http://dev.clojure.org/jira/browse/CLJ-1750.

Happy to see more discussion on this, either here or on CLJ-1750 (which is one idea for a feature to address this, but not likely the only one).


--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at https://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.

Herwig Hochleitner

unread,
May 22, 2016, 10:26:06 PM5/22/16
to cloju...@googlegroups.com, Mike Fikes
2016-05-23 2:01 GMT+02:00 Alex Miller <al...@puredanger.com>:
I've discussed this several times with Rich, David N, Luke, Tim B, and others but we have not (yet) ever reached a consensus of what, if anything, should be done. To be clear, the issue here is that macros are always implemented in Clojure (for both platforms) and macros *produce* code so having reader conditionals don't help you - you want conditional *emitting*.

Another thing to consider would be macros that should work in self-hosted cljs, as well as regular. Maybe Mike Fikes will share his perspective on this, he just ported core.async for self-hosting: https://github.com/mfikes/andare

I'm not sure I'd say the three points below are exactly the way I've seen people handling this although maybe I'm just using a slightly different choice of words/details. In particular, one option is to keep the *macros* (but not the rest of the code) split across platforms (and include the right one in a cljc via reader conditionals), which is maybe somewhere between 1 and 2.

That would be exactly what I meant with #1.
#2 is only an option when a macro emits function invocations, not other (namespace - split) macros.
 
I realize this is easier in some libs/apps than in others. For #3, I've seen several other ways to detect the correct target platform, but they're all tricks of a sort.

There is a ticket in this ballpark at http://dev.clojure.org/jira/browse/CLJ-1750.

Thanks for the link, that's definitely a ticket of interest towards this problem. Basically a formalization of the detection hacks, that people are currently using.
I'm not convinced, that platform information needs to be available at runtime, as proposed in that ticket.
I am convinced that platform information needs to be available during macro expansion, though. That is, information about the target - platform. Maybe information about the compiler - platform as well.

I'm all for keeping it easy and not hitting our users with towers and phases, but I'll still be thinking about whether / how syntax-quote should be aware of the target - platform. Did that point show up in any discussions yet?

Herwig Hochleitner

unread,
May 22, 2016, 10:37:33 PM5/22/16
to cloju...@googlegroups.com
2016-05-23 4:25 GMT+02:00 Herwig Hochleitner <hhochl...@gmail.com>:
2016-05-23 2:01 GMT+02:00 Alex Miller <al...@puredanger.com>:
I'm not sure I'd say the three points below are exactly the way I've seen people handling this although maybe I'm just using a slightly different choice of words/details. In particular, one option is to keep the *macros* (but not the rest of the code) split across platforms (and include the right one in a cljc via reader conditionals), which is maybe somewhere between 1 and 2.

That would be exactly what I meant with #1.
#2 is only an option when a macro emits function invocations, not other (namespace - split) macros.

PS: I see what you were saying there. My perspective was from trying to keep namespaces as compatible as possible. From that perspective, the split between `cljs.core.async` vs `clojure.core.async` isn't essential, while `cljs.core.async.macros` vs `clojure.core.async` is, with current limitations. Sorry that I wasn't clear on that.

Alex Miller

unread,
May 22, 2016, 10:51:55 PM5/22/16
to cloju...@googlegroups.com
On Sun, May 22, 2016 at 9:37 PM, Herwig Hochleitner <hhochl...@gmail.com> wrote:

PS: I see what you were saying there. My perspective was from trying to keep namespaces as compatible as possible. From that perspective, the split between `cljs.core.async` vs `clojure.core.async` isn't essential, while `cljs.core.async.macros` vs `clojure.core.async` is, with current limitations. Sorry that I wasn't clear on that.

Right! Agreed. 

Mike Fikes

unread,
May 23, 2016, 6:53:25 AM5/23/16
to Clojure Dev, mikef...@gmail.com
I'd add: In self-hosted ClojureScript, macro namespaces are compiled as ClojureScript, thus taking the :cljs branch of reader conditionals. (I understand this discussion is about the capabilities of the target of the expansion, and not the capabilities of the language performing the expansion.)

My opinion: There are quite a few subtleties surrounding self-hosted macro namespaces, and it has taken a while to discover them. While self-hosting is not a high priority, I'd suggest considering it as well in these kinds of discussions, and also allowing generous amounts of time for ideas to be explored and for consensus to develop.
Reply all
Reply to author
Forward
0 new messages