7

I want to generate a fn totally at runtime (i.e. the name and the arg symbols are decided at runtime, not in code) What's the best way to achieve this ?

For example how can I implement the following function ?

(defn gen-fn [name arg-symbols body]
...
...

which would be used like this:

(gen-fn "my-func-name" (symbol "x") (symbol "y") (println "this is body. x=" x))

Note that function name, the args and the body are not coded but can be decided at runtime

GabiMe
  • 18,105
  • 28
  • 76
  • 113

3 Answers3

12
(defn gen-fn
  [n as b]
  (let [n        (symbol n)
        as       (vec (map symbol as))
        fn-value (eval `(fn ~n ~as ~b))]
    (intern *ns* n fn-value)))

And some use:

user=> (gen-fn "foo" ["x"] '(do (println x) (println (inc x))))
#'user/foo
user=> (foo 5)
5
6
nil

However, I don't really like this approach. It smells really hard: eval. Why do you want to generate globals at runtime? I see various problems with wrong namespaces and other ugly hiccups rising at the horizon...

kotarak
  • 17,099
  • 2
  • 49
  • 39
  • Yes. This is ugly stuff. The reason I need this is I am experimenting with GeneticProgramming under Clojure. Clojure seems really natural for this. – GabiMe Dec 01 '09 at 09:50
  • Ah. Ok. GP may be a legal use of `eval`. But still stay alert for strange effects with `eval`. – kotarak Dec 01 '09 at 10:38
  • why eval? couldn't you do this with a macro? – Jeremy Wall Dec 01 '09 at 14:45
  • 3
    No, macros run at compile time, not at runtime. –  Dec 03 '09 at 10:21
  • 3
    Great answer. I'd just add that if the purpose if genetic programming then it might make sense to store the generated functions directly in a map rather than add them to a namespace (which could get confusing as it's a big chunk of mutable state...) – mikera May 01 '11 at 15:25
  • @mikera Great comment. I don't know much about genetic programming, but your way to think about it sounds very reasonable! If the functions represent the state of your program, they shouldn't live in the Vars of a namespace, but some of the reference types (minus Vars): atom, ref or agent. – kotarak May 06 '11 at 13:10
1

Another way is using "eval" and "read-string":

user=> (def f1 (eval (read-string "(fn [x y] (* x y))")))

#'user/f1

user=> (f1 3 5)

15

jamesqiu
  • 1,694
  • 1
  • 11
  • 6
0

I'm not entirely sure but I believe you could do this with a macro which would be superior to eval.

(defmacro gen-fn
  [n as b]
  (let [n  (symbol n)
        as (vec (map symbol as))]
    `(intern *ns* n (fn ~n ~as ~@b))))
Jeremy Wall
  • 23,907
  • 5
  • 55
  • 73
  • 2
    This does not work at runtime. The point is: you need to know `n`, `as` and `b` in advance. And then you can do: (defmacro gen-fn [n as b] `(defn ~(symbol n) ~(vec (map symbol as)) ~@b)). With the `eval`-approach the arguments to `gen-fn` can be results of arbitrary computations. – kotarak Dec 01 '09 at 14:59