apply for macros?

101 views
Skip to first unread message

b2m

unread,
Oct 2, 2009, 12:47:10 PM10/2/09
to Clojure
Hi everbody,

I am having some problems using macros which I will explain by using
the core macro 'and'.

Imagine having a macro accepting multiple arguments: (and true false
true false)

But all the arguments are in a list e.g. (def arglist '(true false
true false))

Is there a simple way to 'apply' the macro to a list of arguments like
it works for functions: (apply + '(1 2 3)) ?

Sincerely
Benjamin
PS: I know there would be a solution for this special problem with and
by using filters on the list. But this is just an example on the
problem of applying a macro on a list.

Stuart Sierra

unread,
Oct 2, 2009, 1:26:03 PM10/2/09
to Clojure
On Oct 2, 12:47 pm, b2m <b2monl...@googlemail.com> wrote:
> Is there a simple way to 'apply' the macro to a list of arguments like
> it works for functions: (apply + '(1 2 3)) ?

Nope, no can do.

For an example of why, check out clojure.contrib.apply-macro -- the
warnings are there for a reason.

"apply" is a function, so it's evaluated at runtime. But macros are
evaluated at compile-time. If you're calling it on a list of
arguments, that list won't exist until runtime.

-SS

b2m

unread,
Oct 2, 2009, 2:54:34 PM10/2/09
to Clojure
Hi Stuart,

> Nope, no can do.
>
> For an example of why, check out clojure.contrib.apply-macro -- the
> warnings are there for a reason.

For some reason I missed this library. Looks the same like the code I
wrote and declared as "to evil to use".

> "apply" is a function, so it's evaluated at runtime.  But macros are
> evaluated at compile-time.  If you're calling it on a list of
> arguments, that list won't exist until runtime.

Thx for this explanation.

Greets, Benjamin

b2m

unread,
Oct 3, 2009, 6:19:29 AM10/3/09
to Clojure
Hi,
some piece of code that helps me in this special case.
Here the "translated" version:

(defmacro apply-macro
[m & args]
`(list* ~m ~@args))

(def li '(true false false))
(eval (apply-macro 'and true false li)) ; observe of the quote before
the macro

Or instead of trying sth. like:

(defn some-test [x & args] (if-not (nil? x) (apply and args))) ; this
will fail

writing this function as macro:

(defmacro some-test [x & args] `(if-not (nil? ~x) (and ~@args) false))

Somebody with other ideas?

Greets, Benjamin

Rob

unread,
Oct 3, 2009, 10:45:43 AM10/3/09
to Clojure


On Oct 2, 12:47 pm, b2m <b2monl...@googlemail.com> wrote:
>
> But all the arguments are in a list e.g. (def arglist '(true false
> true false))
>
> Is there a simple way to 'apply' the macro to a list of arguments like
> it works for functions: (apply + '(1 2 3)) ?
>

Yes. You build up the code/data structure and pass it to either
`eval' or `macroexpand', depending on your exact goals...

user=> (def args '(true false true false))
#'user/args
user=> (def code `(and ~@args))
#'user/code
user=> code
(clojure.core/and true false true false)
user=> (eval code)
false
user=> (macroexpand code)
(let* [and__3981__auto__ true] (if and__3981__auto__ (clojure.core/and
false true false) and__3981__auto__))


Rob

b2m

unread,
Oct 3, 2009, 2:18:05 PM10/3/09
to Clojure
Hi
> Yes.  You build up the code/data structure and pass it to either
> `eval' or `macroexpand', depending on your exact goals...

thx

> user=> (def args '(true false true false))
> #'user/args
> user=> (def code `(and ~@args))
> #'user/code
> user=> code
> (clojure.core/and true false true false)
> user=> (eval code)
> false
> user=> (macroexpand code)
> (let* [and__3981__auto__ true] (if and__3981__auto__ (clojure.core/and
> false true false) and__3981__auto__))

A solution not using eval would be great but when writing code that
writes code this maybe is impossible.

Greets, Benjamin

John Harrop

unread,
Oct 3, 2009, 1:22:55 PM10/3/09
to clo...@googlegroups.com
In the specific cases of "and" and "or", I made utility functions that do non-short-circuiting "and" and "or" for use with "apply" and a stream of boolean data. (Not sure which implementation is more efficient though: a version that returns its argument with one argument, punts to the appropriate macro with two, and uses recur with more than two; a version similar to that that uses reduce with more than two; or a higher-level (every? identity args) / (some? identity args). The latter are clearly more elegant, but speed is a concern with something that might be used often.)

What macros do y'all have that you want to "apply" things to? Most likely what you need is to make a version that's a function, if you can live without some deferred/selective evaluation of arguments type feature (like the short-circuiting of "and").

b2m

unread,
Oct 3, 2009, 6:50:12 PM10/3/09
to Clojure
> What macros do y'all have that you want to "apply" things to?

I am using structs and functions for workings with these structs.

Just some very general example:

(defstruct department :name :head :members-l1 :members-l2 ...)

(defn process-department [department-struct]
(reduce + (list
(* members-l1-fee (:members-l1 department-
struct))
(* members-l2-fee....)))

The number of "payment levels" are not the same for every "company",
so I thought of creating these structs and functions by macros.
Because they all have the same arguments these calls are wrapped in a
function:

(defn init-funs [name & levels]
(do
(apply-macro create-department-struct name levels)
(apply-macro create-process-department name levels)
nil))

A call like
(init-funs "company1" "level1" "level2")
will result in
(defstruct company1-department :name :head :level1 :level2)
and so on.

So I need macros for the benefit of individual strucs and functions
for every "company", and I need something like apply to pass
parameters from the inital function to the macros. This is working by
using (eval (apply-macro... as described in a previous posting in this
thread. It would be nice to have a solution without eval. But maybe my
attempt of writing code that writes code is too enthusiastic.

Meikel Brandmeyer

unread,
Oct 4, 2009, 2:31:19 AM10/4/09
to clo...@googlegroups.com
Hi,

Am 04.10.2009 um 00:50 schrieb b2m:

>> What macros do y'all have that you want to "apply" things to?

A sure code smell, IMO. It most likely is based on a misunderstanding
what macros are capable to do.

> I am using structs and functions for workings with these structs.
>
> Just some very general example:
>
> (defstruct department :name :head :members-l1 :members-l2 ...)
>
> (defn process-department [department-struct]
> (reduce + (list
> (* members-l1-fee (:members-l1 department-
> struct))
> (* members-l2-fee....)))

The functions themselves can be easily made independent from the
number of keys. Just save the keys in a constant.

(defn process-department
[department-struct]
(->> +payment-levels+
(map #(* (fee %) (department-struct %)))
(reduce +)))

So you actually don't need specific functions for different companies.
You just need some metadata on the struct.

> The number of "payment levels" are not the same for every "company",
> so I thought of creating these structs and functions by macros.
> Because they all have the same arguments these calls are wrapped in a
> function:
>
> (defn init-funs [name & levels]
> (do
> (apply-macro create-department-struct name levels)
> (apply-macro create-process-department name levels)
> nil))

Here we have the smell! You cannot define functions with a function.
You have to use a macro! It would look like this:

(defmacro init-department
[name & levels-and-fees]
(let [fee (apply hash-map levels-and-fees)]
`(do
(def ~'fee ~fee)
(defstruct ~name ~@(keys fee))
(def ~'payment-levels ~(set (keys fee))))))

Note, the "apply-macro" in form of ~@.

> So I need macros for the benefit of individual strucs and functions
> for every "company", and I need something like apply to pass
> parameters from the inital function to the macros. This is working by
> using (eval (apply-macro... as described in a previous posting in this
> thread. It would be nice to have a solution without eval. But maybe my
> attempt of writing code that writes code is too enthusiastic.

Requiring eval is a code stink. It is a sign, that some pieces of the
puzzle don't really fit together. Whenever you arrive at the need of
eval in "normal" code, you should step back and look where the
misunderstanding is.

Hope this helps.

Sincerely
Meikel

b2m

unread,
Oct 4, 2009, 5:16:43 AM10/4/09
to Clojure
Hi,

On 4 Okt., 08:31, Meikel Brandmeyer <m...@kotka.de> wrote:
> The functions themselves can be easily made independent from the  
> number of keys. Just save the keys in a constant.
>
> (defn process-department
>    [department-struct]
>    (->> +payment-levels+
>      (map #(* (fee %) (department-struct %)))
>      (reduce +)))
>
> So you actually don't need specific functions for different companies.  
> You just need some metadata on the struct.

The function "process-department" will be executed about 100.000 times
in one run. So I am trying to avoid macros and lamdas, and also
transfering as much information as possible into the structure of the
function to become faster. This is another reason for creating
customized functions by macros (hope this does not offend the meaning
of macros). Anyway, I will keep this solution at the back of my mind.

> Here we have the smell! You cannot define functions with a function.
> You have to use a macro! It would look like this:
>
> (defmacro init-department
>    [name & levels-and-fees]
>    (let [fee (apply hash-map levels-and-fees)]
>       `(do
>         (def ~'fee ~fee)
>         (defstruct ~name ~@(keys fee))
>         (def ~'payment-levels ~(set (keys fee))))))
>
> Note, the "apply-macro" in form of ~@.

You've made my day! I always tried code like:

(defmacro init-department
[name & levels-and-fees]
(let [fee (apply hash-map levels-and-fees)]
(do
`(def ~'fee ~fee)
`(defstruct ~name ~@(keys fee))
`(def ~'payment-levels ~(set (keys fee))))))

This would expand to the last statement, ignoring the first two. So I
started searching an "apply-macro" solution.

> Requiring eval is a code stink.
Agree.

> It is a sign, that some pieces of the  
> puzzle don't really fit together. Whenever you arrive at the need of  
> eval in "normal" code, you should step back and look where the  
> misunderstanding is.

This was my reason for starting this thread =)

> Hope this helps.

Yes, it does!

thx @all, Benjamin

John Harrop

unread,
Oct 3, 2009, 10:40:38 PM10/3/09
to clo...@googlegroups.com
On Sat, Oct 3, 2009 at 6:50 PM, b2m <b2mo...@googlemail.com> wrote:
> What macros do y'all have that you want to "apply" things to?
(defn init-funs [name & levels]
  (do
   (apply-macro create-department-struct name levels)
   (apply-macro create-process-department name levels)
   nil))

A call like
(init-funs "company1" "level1" "level2")
will result in
(defstruct company1-department :name :head :level1 :level2)
and so on.

So I need macros for the benefit of individual strucs and functions
for every "company", and I need something like apply to pass
parameters from the inital function to the macros. This is working by
using (eval (apply-macro... as described in a previous posting in this
thread. It would be nice to have a solution without eval. But maybe my
attempt of writing code that writes code is too enthusiastic.

Not really. But since you're automating generation of deffoos (in this example, defstructs) you can, and should, use macros all the way down. I.e. init-funs can, and should, be a macro above and it can then just be:

(defmacro init-funs [name & levels]
  `(do
    (create-department-struct ~name ~@levels)
    (create-process-department ~name ~@levels)
    nil))

or similarly as the case may be.

If you need to be creating these things dynamically, with information only available at runtime, defstruct is probably the wrong tool for the job, or the only struct member should be :name, and the levels at least should just be ordinary map keys (not struct keys); then you can dispense with init-funs entirely, and replace the call currently used to get a new instance of the struct with a call that given a name, figures out the appropriate levels (from a name to level-name-list hashmap you maintain at runtime) and initializes a structmap with the :name struct-key set appropriately and also empty lists for each level-name key appropriate for that name.

Or something like that.

b2m

unread,
Oct 4, 2009, 2:55:04 PM10/4/09
to Clojure
Hi,

On 4 Okt., 04:40, John Harrop <jharrop...@gmail.com> wrote:

> If you need to be creating these things dynamically, with information only
> available at runtime, defstruct is probably the wrong tool for the job, or
> the only struct member should be :name, and the levels at least should just
> be ordinary map keys (not struct keys); then you can dispense with init-funs
> entirely, and replace the call currently used to get a new instance of the
> struct with a call that given a name, figures out the appropriate levels
> (from a name to level-name-list hashmap you maintain at runtime) and
> initializes a structmap with the :name struct-key set appropriately and also
> empty lists for each level-name key appropriate for that name.

I was understanding and using strucs as helpers for creating maps,
sharing the same keys.
Time for reading some clojure books again.

Greets, Benjamin

cody koeninger

unread,
Oct 5, 2009, 1:29:57 PM10/5/09
to Clojure
On Oct 4, 1:31 am, Meikel Brandmeyer <m...@kotka.de> wrote:
> Here we have the smell! You cannot define functions with a function.  
> You have to use a macro!

I am not clear on what you mean by this. From a user's point of view,
what is the difference between defining a function, and interning a
var with a fn object as its value?

user> (defn define-function [name] (intern *ns* name (fn [] (str
"called a generated function: " name))))
#'user/define-function
user> (some-function)
; Evaluation aborted.
user> (define-function 'some-function)
#'user/some-function
user> (some-function)
"called a generated function: some-function"

Or is your point just that #'fn is a macro?

Meikel Brandmeyer

unread,
Oct 5, 2009, 2:38:50 PM10/5/09
to clo...@googlegroups.com
Hi,

I stand corrected.

Sincerely
Meikel

John Harrop

unread,
Oct 5, 2009, 3:16:43 PM10/5/09
to clo...@googlegroups.com
I'd argue that you can define a function within a function, but that interning a var within a function is a smell, and doing it with a constant, rather than argument-supplied or computed, name, in a function or in a macro, is a stench (e.g. (defn foo [x y z] (defn bar ... )))

Reply all
Reply to author
Forward
0 new messages