I think you have the right idea, only not quite. :) If you write a
macro to define a function whose name is determined at run-time, you
end up with either a function that your program will never refer to,
or a program that calls functions that may or may not exist, depending
on what run-time values were passed to your macro. I don't think
that's really what you want. You want functions you can explicitly use
in your code while you are writing it, but you want the *behavior* of
those functions to be determined at runtime.

What I think you really want are anonymous functions assigned to named
vars--the function itself is generated at runtime and uses closures to
create the customized functionality you want, but you use the
generically-named vars to refer to them in your code. (Oops, I'm
saying "vars" and that may not be the right technical term in this
context--but you know what I mean.)

So for example, here's a "factory" function that takes an argument and
returns an anonymous function with your argument built-in:

(defn make-draw-fn [page field loc]
 (fn [offset-x offset-y]
   (let [gui (get-field page field loc)
         bounds (.getRect field)
         bounds (.addOffset offset-x offset-y)]
     (.doDraw gui bounds))))

The (let ...) form inside make-draw-fn does *not* execute when you
call make-draw-fn. The make-draw-fn merely *returns* a function that
will execute its body when you call it. What makes this particularly
useful is that when it does execute, it will remember the values of
page, field, and loc. In other words, you're creating a function at
runtime, and its behavior is customized by the runtime arguments you
pass it.

Then you make a page full of fields like this:

(let [page "registration"
      controls { :username (make-draw-fn page "username" "id=Username")
                    :password (make-draw-fn page "password" "id=Password")
                    ; ...
                   }]
   ((controls :username) 0 0)
   ((controls :password) 0 0)
   ; ...
)

This is really rough code (and my own noobishness is probably
showing), but I'm just trying to illustrate the general idea. By
making a factory function that returns anonymous functions, you create
closures that will "remember" any arguments passed to the factory.
This achieves the run-time customization you want to use macros for,
but without using a macro. Then you assign that anonymous function to
a variable of some kind, and call it by using the extra parentheses,
as in "((controls :password) 0 0)".    I put the anonymous functions
in a hash map, but you could just as easily assign them to a var or a
list or anything else.

If you can get that working, but you find it's really annoying to
write the extra parentheses in "((controls :password) 0 0)," *then*
you might want to consider writing a macro to expand "(password 0 0)"
into "((controls :password) 0 0)". At that point you'll be good,
because you'll know all the names at the time you write your code, and
you won't have to mess with evals or anything.

HTH

Mark


On Fri, Jun 3, 2011 at 10:36 AM, nil <ache...@gmail.com> wrote:
>> The problem here is that macros run at compile time, but let bindings
>> exist at run time.
>>
>> If you need the name to be determined at run time you will need to use eval.
>
> Where do I use eval? I tried looking at the argument to see if it was
> called with a string literal vs a symbol, but can't eval a local:
>
> (defmacro foo [x]
>  (let [name-as-string# (if (symbol? x) (eval x) x)
>        name-as-symbol# (symbol name-as-string#)]
>    `(defn ~name-as-symbol# [] ())))
>
> (let [eff "gee"] (foo eff))
>
>> If you don't need the name at run time, why are you using (let [eff
>> "gee"] (foo eff)) and not simply (foo gee)?
>
> I'm using let because I'd like to have the name at run time. I'm
> defining families of functions to manipulate types of UI elements by
> saying (type page-name field-name locator). Without knowing a solution
> to my problem, I'm stuck saying the following in order to generate a
> bunch of functions to use later in my program...
>
> (txt "registration" "username" "id=Username")
> (txt "registration" "password" "id=Password")
> (tog "registration" "terms" "id=AcceptTerms")
> (nav "registration" "submit" "//html/body/div/form/div/input" "/
> SignedIn")
>
> ... Instead of this:
>
> (let [page "registration"]
>  (txt page "username" "id=Username")
>  (txt page "password" "id=Password")
>  (tog page "terms" "id=AcceptTerms")
>  (nav page "submit" "//html/body/div/form/div/input" "/SignedIn"))
>
> ... or interning page as a symbol that sticks around after I'm done
> with the definitions? Ew. Un-interning afterwards? Hm.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@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+unsubscr...@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 post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to