Calling from a macro to a private macro

597 views
Skip to first unread message

Yoav Rubin

unread,
Mar 17, 2014, 2:02:06 AM3/17/14
to clo...@googlegroups.com
Hi All,

I have a namespace that has two macros as part of its public API, and another macro that act as helpers for the public macro

(defmacro helper-mac [arg1 arg2 f]
;; do stuff with f , arg1 and arg2
)

(defmacro m0 [arg1 arg2]
   (priv-mac arg1 arg2 f1)
)
 
(defmacro m1 [arg1 arg2] (
  (priv-mac arg1 arg2 f2)
)

f1 and f2 are just two functions.

I would like to make the helper macro private (using  ^:private), but when I do it and call either m0 or m1 from another namespace, I get an exception saying that helper-mac is private.

Is it possible to call from to a macro in another namespace when that macro is calling a private macro in its namespace?

Thanks,

Yoav

Maik Schünemann

unread,
Mar 17, 2014, 10:09:23 AM3/17/14
to clo...@googlegroups.com
Hi,
I guess that maybe the same trick works for calling private functions
from another namespace via getting the var and calling it.
That being said, macros calling macros gets very complex after a short
time, it is better to have helper functions instead of macros.
The functions just accept s-expressions and return s-expressions. I
find myself doing exactly that for nontrivial macros.

Hope that helps,
Maik
> --
> 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.

James Reeves

unread,
Mar 17, 2014, 10:19:19 AM3/17/14
to clo...@googlegroups.com
Don't use a private macro: use a function that spits out an s-expression.

- James


--

Yoav Rubin

unread,
Mar 17, 2014, 1:31:36 PM3/17/14
to clo...@googlegroups.com, ja...@booleanknot.com
I need to do it, as I need the arguments to remain not evaluated until they get to that private macro. That private macro does some work on the arguments before they get evaluated (the arguments themselves are s-expressions).

Still, even if it is a private function - how can I call it from a macro that is called from another namespace?

Yoav Rubin

unread,
Mar 17, 2014, 1:33:57 PM3/17/14
to clo...@googlegroups.com
I'm familiar with that trick, but is it a language feature (and then I can rely on to not being changed in the future)? or is it a hack that based on something that may change in future releases?

Thanks,

Yoav

Timothy Baldridge

unread,
Mar 17, 2014, 1:40:42 PM3/17/14
to clo...@googlegroups.com
Don't use private vars. Instead move the macro to a sub namespace called "internals" or "impl" or something like that and make it public. Prefer trusting your users instead of limiting them. 

my $0.02

Timothy
--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

Yoav Rubin

unread,
Mar 17, 2014, 6:29:52 PM3/17/14
to clo...@googlegroups.com
Let's leave aside the recommendations not to do it. I'd like to understand why I get the exception when I make such a call (from a public macro to a private macro), and if it is possible, what am I doing wrong here...

Timothy Baldridge

unread,
Mar 17, 2014, 6:40:36 PM3/17/14
to clo...@googlegroups.com
The problem is in the way lisp compilers work. Example:

The public macro gets analyzed first and runs, and emits the code required to call the private macro. Then the compiler analyzes the output of the first macro and discovers that the code now calls a new macro. It analyzes this code and finds a call to a private var and dies. 

Macros are evaluated one at a time outer forms to inner forms. This is pretty much the opposite from normal evaluation that happens from the inside out. 

Timothy

Yoav Rubin

unread,
Mar 17, 2014, 6:55:36 PM3/17/14
to clo...@googlegroups.com
Thanks - that's just what I needed to know :-)

Michael Blume

unread,
Mar 17, 2014, 7:38:59 PM3/17/14
to clo...@googlegroups.com, ja...@booleanknot.com
You don't have the macro generate a call to the private function, you have the macro call the private function directly

replace:

(defmacro call-self* [x]
  `(~x ~x))

(defmacro call-self [x]
  `(do
    (println "calling form " ~(str x) " with itself")
    (call-self ~x)))

with:

(defn- call-self* [x]
  `(~x ~x))

(defmacro call-self [x]
  `(do
    (println "calling form " ~(str x) " with itself")
    ~(call-self x)))

The function call-self* is still called at compile-time and is called *by the call-self macro*, not the generated client code. Make sense?

James Reeves

unread,
Mar 17, 2014, 8:32:35 PM3/17/14
to Yoav Rubin, clo...@googlegroups.com
You can call functions within the macro body without the arguments to the macro being evaluated. If this wasn't the case, you'd be very limited in how you could transform code.

- James

Yoav Rubin

unread,
Mar 18, 2014, 12:41:12 AM3/18/14
to clo...@googlegroups.com, ja...@booleanknot.com
The case is that there are two public macro that call the same private helper macro, and that private macro is the one who does the heavy lifting, so I can either duplicate that private macro's code to be inside the public macros, or make it public and mark it somehow with a "do-not-touch" sign. 

Jason Felice

unread,
Mar 18, 2014, 12:07:58 PM3/18/14
to clo...@googlegroups.com
I think you're missing an important part:

We're saying that you can take your third, private helper macro, and turn it into a private helper function.  If you invoke it at compile time from the public macros, it will receive the unevaluated forms you want, and be able to transform the unevaluated S-expression the way you'd like.

Instead of:

(defmacro helper
   [s-expr]
   (reverse s-expr))

(defmacro p1
    [s-expr]
    `(foo (helper s-expr) bar))

(defmacro p2
    [s-expr]
    `(baz ~(helper s-expr) quux))

You would have:

(defn ^:private helper
   [s-expr]
   (reverse s-expr))

(defmacro p1
   [s-expr]
   `(foo ~(helper s-expr) bar))

(defmacro p2
  [s-expr]
  `(baz ~(helper s-expr) quux))

(macroexpand (p1 (a b)) ;=> (foo (b a) bar)
(macroexpand (p2 (c d)) ;=> (baz (d c) quux)

Once you've entered a macro, you're evaluating normal expressions, and you can call functions normally to manipulate the expressions.


Reply all
Reply to author
Forward
0 new messages