Generating functions programmatically

553 views
Skip to first unread message

icemaze

unread,
Sep 10, 2010, 3:39:27 PM9/10/10
to Clojure
Hi, I'm developing a small DSL with Clojure and I need to define many
similar functions. I'd like to do that programmatically, of course.

My solution (involving a simple macro) doesn't work, so I won't bother
you with it. I'll post it if anyone asks.

Basically what I need is: given a list of keywords, for each keyword x
i need to define a function whose name is (str "prefix-" (name x)).
The function has a very short body which uses x in one place
(something like (fn [n] (= n x))).

Can you help me please? I've been banging my head against this for
over four hours and I'm getting a little frustrated.

Thanks.

Alan

unread,
Sep 10, 2010, 4:02:55 PM9/10/10
to Clojure
I actually did this just the other day, to create a simple C-style
enum macro (I assume someone has a better version; this was a learning
exercise). You can see my project at www.github.com/amalloy/enum. It
sounds like your problem might be constructing the symbol to define;
the solution will look something like:

(let [fn-sym (->> name (str "prefix-") symbol)]
`(defn ~fn-sym [args] body))

Alan

unread,
Sep 10, 2010, 4:08:59 PM9/10/10
to Clojure
SPOILER BELOW. I'm not sure how much help you want, so I went ahead
and wrote your macro. Whitespace padding so that you won't see it if
you don't want the whole solution:











user=> (defmacro make-fn [key]
(let [sym (->> key name (str "synthetic-") symbol)]
`(defn ~sym [n#] (= n# ~key))))
#'user/make-fn
user=> (make-fn :a)
#'user/synthetic-a
user=> (synthetic-a :q)
false
user=> (synthetic-a :a)
true
user=> (macroexpand '(make-fn :a))
(def synthetic-a (.withMeta (clojure.core/fn synthetic-a
([n__1041__auto__] (clojure.core/= n__1041__auto__ :a))) (.meta (var
synthetic-a))))

On Sep 10, 1:02 pm, Alan <a...@malloys.org> wrote:
> I actually did this just the other day, to create a simple C-style
> enum macro (I assume someone has a better version; this was a learning
> exercise). You can see my project atwww.github.com/amalloy/enum. It

icemaze

unread,
Sep 10, 2010, 4:37:16 PM9/10/10
to Clojure
Alan, thank you for your reply.
Unfortunately your solution is very similar to mine and it suffers
from the same problem (maybe I'm using it incorrectly, I don't know).
If I write:

(doseq [x '(:a :b)]
(make-fn x))

it defines a single function "synthetic-x". Is there a way to make
this work? I tried everything but both eval and var-get don't work for
local bindings.

Thanks again.

Hubert Iwaniuk

unread,
Sep 10, 2010, 5:40:19 PM9/10/10
to clo...@googlegroups.com
Hi icemaze,

Please look at how fns are generated in http.async.client http://github.com/neotyk/http.async.client/blob/master/src/http/async/client/util.clj#L54
this macro is later used here: http://github.com/neotyk/http.async.client/blob/master/src/http/async/client.clj#L43

HTH,
Hubert.

> --
> 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

Alan

unread,
Sep 10, 2010, 6:07:56 PM9/10/10
to Clojure
I see. It's hard to imagine how this could work, since macros don't
have access to runtime data like the value of x in your example.
Perhaps you're better off writing a function that returns a closure,
and iteratively def'ing those?

user=> (defn make-kw-fn [kw]
#(= kw %))
#'user/make-kw-fn
user=>
(def kws [:a :b])
#'user/kws
user=>
(eval (cons 'do (map #(list 'def (symbol (str "test-" (name %))) (make-
kw-fn %)) kws)))
#'user/test-b
user=> (test-b :a)
false
user=> (test-b :b)
true

Alan

unread,
Sep 10, 2010, 6:17:02 PM9/10/10
to Clojure
PS this is super-ugly and I'm always embarrassed when I find myself
using eval in a lisp. While this works, I'd love it if someone could
tell me how to do it with macros.

Robert McIntyre

unread,
Sep 10, 2010, 7:20:02 PM9/10/10
to clo...@googlegroups.com
Why not explicitly feed the macro the keyword map if that is what it wants?

(defmacro ultra-synth [prefix keywords]
(let [symbols (map (comp symbol name) keywords)
fn-names (map (comp symbol
(partial str prefix) name) keywords)
defn-forms (map
(fn [fn-name symbol]
`(defn ~fn-name [n#]
(= n# ~symbol)))
fn-names symbols)]
`(do ~@defn-forms)))

thunderbolt.play> (def a 5)
#'thunderbolt.play/a
thunderbolt.play> (def b 5)
#'thunderbolt.play/b
thunderbolt.play> (ultra-synth "hi" [:a :b])
#'thunderbolt.play/hib
thunderbolt.play> (hia 5)
true

--Robert McIntyre

Alan

unread,
Sep 10, 2010, 8:20:23 PM9/10/10
to Clojure
It sounds like he's going to be given the list of keywords by the
user, and won't have it as a compile-time literal. Maybe he's load'ing
a .clj file, for example, that someone will write in the future. Maybe
you can combine our two approaches to get something that works and
isn't ugly, but I'm having a hard time finding it. (def) requires a
symbol as its first argument, and is a macro so needs it at compile
time. The best I can do is:

(defmacro buildfn [name]
`(defn ~name [arg#] 10))
(doseq [k '[a b c]]
(eval `(buildfn ~k)))

On Sep 10, 4:20 pm, Robert McIntyre <r...@mit.edu> wrote:
> Why not explicitly feed the macro the keyword map if that is what it wants?
>
> (defmacro ultra-synth [prefix keywords]
>   (let [symbols (map (comp symbol name)  keywords)
>         fn-names (map (comp symbol
>                             (partial str prefix) name) keywords)
>         defn-forms (map
>                     (fn [fn-name symbol]
>                       `(defn ~fn-name [n#]
>                          (= n# ~symbol)))
>                     fn-names symbols)]
>     `(do ~@defn-forms)))
>
> thunderbolt.play> (def a 5)
> #'thunderbolt.play/a
> thunderbolt.play> (def b 5)
> #'thunderbolt.play/b
> thunderbolt.play> (ultra-synth "hi" [:a :b])
> #'thunderbolt.play/hib
> thunderbolt.play>  (hia 5)
> true
>
> --Robert McIntyre
>

icemaze

unread,
Sep 10, 2010, 8:24:29 PM9/10/10
to Clojure
Yeah, I guess I could use a macro to generate the "call" to the other
macro in a way similar to how you used it. Thank you, that should
definitely work. I'll try it right now.

icemaze

unread,
Sep 10, 2010, 8:24:51 PM9/10/10
to Clojure
I agree: eval never looks pretty.

Robert McIntyre

unread,
Sep 10, 2010, 8:29:51 PM9/10/10
to clo...@googlegroups.com
there's always apply-macro from contrib for doing perverse stuff like
that, so you don't
have to *see* eval if you don't want to :)

--Robert McIntyre

On Fri, Sep 10, 2010 at 8:24 PM, icemaze <ice...@gmail.com> wrote:
> I agree: eval never looks pretty.
>

icemaze

unread,
Sep 10, 2010, 8:34:41 PM9/10/10
to Clojure
Yes, I think I'll have to pass the keywords as literals. I don't think
there's a way around that (other than using eval, as per Alan's
solution).
I was too excited about Hubert's hint and I found myself in the exact
same problem with the second macro.

icemaze

unread,
Sep 10, 2010, 8:55:53 PM9/10/10
to Clojure
You are mostly right in your assumptions: I could dump the keywords in
the clj as literals but it would be tedious and not elegant at all.
Eval's not pretty but it works; plus it's there for a reason, like
working around the shortcomings of the language (and of my brain).

I was about to post my solution but I won't, since it looks exactly
like yours.

Thank you guys for your efforts!

Btsai

unread,
Sep 10, 2010, 5:41:05 PM9/10/10
to Clojure
This is probably not the prettiest way to do it, but I think it gets
the job done:

(defn make-sym [keyword]
(->> keyword name (str "prefix-") symbol))

(defn make-fn [keyword]
(let [n (gensym)]
(list 'defn (make-sym keyword) [n] (list '= n keyword))))

(defmacro make-fns [keywords]
`(do ~@(map make-fn keywords)))

user=> (make-fns [:a :b :c])
#'user/prefix-c
user=> (prefix-a :a)
true
user=> (prefix-a :x)
false
user=> (prefix-b :b)
true
user=> (prefix-b :x)
false
user=> (prefix-c :c)
true
user=> (prefix-c :x)
false

The credit belongs to Alan, and Mr. Stuart Halloway for his examples
from Ch. 7 of Programming Clojure.

Robert McIntyre

unread,
Sep 10, 2010, 9:24:13 PM9/10/10
to clo...@googlegroups.com
That is very elegant but has the exact same problem in that the macro
must be called on a literal vector of keywords.

--Robert McIntyre

Btsai

unread,
Sep 10, 2010, 9:49:09 PM9/10/10
to Clojure
I'm sorry, but despite reading through the rest of the thread, it's
not clear to me why that is a problem.

icemaze, could you elaborate on what your use case is? I think with
all of our powers combined, we can come up with something that fits
your needs :)

icemaze

unread,
Sep 11, 2010, 10:29:49 AM9/11/10
to Clojure
Hi Btsai, thank you for your offer for help.

As I said before I *could* use literals but it wouldn't be convenient.
I have a big structure which contains information about "types" (they
are types of domain-specific objects). I would like to extract the
"methods" I need from this structure and define them
programmatically.

Previous solutions to this problem (both by me and other helpful
posters) required to pass keywords as literals. While I could do that,
maintaining a separate list would be a hassle.
Right now I'm using:

(doseq [t (an-expession-which-extracts-a-list-of-keywords)]
(eval `(defobjecttype ~t)))

So the problem is solved for me, although I have to use eval. I'm not
sure exactly how dirty this trick is and especially *why* it is
considered a "smell". I read it on Paul Graham's "On Lisp" and he
vehemently opposes its use but he doesn't explain why or where it is
acceptable. Note that he also considered Common Lisp "let*" a smell,
which is standard practice in Clojure (and in fact there's no
equivalent of Common Lisp "let"). So maybe we are just making too much
a big deal of this "eval" thing.

I feel however that this problem should be addressed by the macro
system somehow (although maybe that's not possible by design).
If someone could find a solution which doesn't involve eval it would
definitely be more elegant.

Nicolas Oury

unread,
Sep 11, 2010, 10:52:16 AM9/11/10
to clo...@googlegroups.com
> So the problem is solved for me, although I have to use eval. I'm not
> sure exactly how dirty this trick is and especially *why* it is
> considered a "smell". I read it on Paul Graham's "On Lisp" and he
> vehemently opposes its use but he doesn't explain why or where it is
> acceptable. Note that he also considered Common Lisp "let*" a smell,
> which is standard practice in Clojure (and in fact there's no
> equivalent of Common Lisp "let"). So maybe we are just making too much
> a big deal of this "eval" thing.
>


It is considered bad because eval is for dynamic evaluation. In this
case you use it at macro time, which shouldn't be necessary most of
the time.

(defn my-complex-function [] ....)


(defmacro insert-the-result-of-the-complex-function []
(my-complex-function))

In your case, as often for strange macro, first write a function that
does the job:

(defn extract-keyword-and-generate-code [].....)

(defmacro generate-methods []
`(do
~@(extract-keyword-and-generate-the-code)))

If you have to use an eval, I find it nicer to use it to compute the
code, not to actually define things.
(defmacro generate-methods [expr]
`(do
~@(extract-keyword-and-generate-the-code (eval expr))))

That way you can often work the eval out, by limiting the kind of
possible exprs.
(If it is only a symbol or a list of keywords, for example)

Anyway, that's just a suggestion. Your solution works too.

Best,

Nicolas.

Kent

unread,
Sep 11, 2010, 11:15:50 AM9/11/10
to Clojure
The fact that you are trying to generate functions based on data that
is available at run time rather than at compile time is a signal that
you should probably be using functions rather than macros to do what
you want. Here is one way:

(defn make-fn [kwd]
(fn [n] (= n kwd)))

(defn intern-fn [kwd]
(let [fn-name (symbol (str "prefix-" (name kwd)))]
(intern *ns* fn-name (make-fn kwd))))

Kent

Tim Daly

unread,
Sep 11, 2010, 12:02:13 AM9/11/10
to clo...@googlegroups.com, Kent
Actually what you seem to be trying to do is create a new kind
of defn function which creates named functions as a side-effect.
This is similar to the common lisp defstruct function which
creates accessors. Define your own "defn" function, such as
"defthing" and let it do the side-effects for you.

Btsai

unread,
Sep 11, 2010, 3:09:07 PM9/11/10
to Clojure
Ah ok. I couldn't come up with anything, but I think Kent has a nice
eval-free (and macro-free) solution.

My thanks to you, and everyone who chimed in, for helping me better
understand the read-time/compile-time/run-time distinction.

icemaze

unread,
Sep 11, 2010, 9:18:34 PM9/11/10
to Clojure
Yes, Kent's solution is spot on! Thank you all for your insights, I
believe we have a winner.
Reply all
Reply to author
Forward
0 new messages