5

I'd like to pre-store a bunch of function calls in a data structure and later evaluate/execute them from within another function.

This works as planned for functions defined at namespace level with defn (even though the function definition comes after my creation of the data structure) but will not work with functions defined by let [name (fn or letfn inside the function.

Here's my small self-contained example:

(def todoA '(funcA))
(def todoB '(funcB))
(def todoC '(funcC))
(def todoD '(funcD)) ; unused

(defn funcA [] (println "hello funcA!"))

(declare funcB funcC)

(defn runit []
    (let [funcB (fn [] (println "hello funcB"))]
    (letfn [(funcC [] (println "hello funcC!"))]
        (funcA)       ; OK
        (eval todoA)  ; OK
        (funcB)       ; OK
        (eval todoB)  ; "Unable to resolve symbol: funcB in this context" at line 2
        (funcC)       ; OK
        (eval todoC)  ; "Unable to resolve symbol: funcC in this context" at line 3
)))

In case you're wondering about my test setup, to see the result of those 6 statements I comment/uncomment specific of the OK/failing lines and then call (runit) from the REPL.

Is there a simple fix I could undertake to get eval'd quoted calls to functions to work for functions defined inside another function?


Update:

This (based on danlei's suggestion) does work. Let's see if I can get this method working in "real life!"

(def todoB '(funcB))
(declare funcB)

(defn runit []
  (binding [funcB (fn [] (println "hello funcB"))]
    (funcB)
    (eval todoB)  ; "Unable to resolve symbol: funcB in this context" at line 1!
))

Update:

This code is going into my solution for a Constraint Satisfaction Problem - I want to find out who owns the zebra! I'm fairly new to Clojure and especially functional programming, and this has made the exercise quite challenging. I'm falling into a lot of pits but I'm OK with that as it's part of the learning experience.

I used to specify the constraints as a bunch of simple vectors, like this:

[:con-eq :spain :dog]
[:abs-pos :norway 1]
[:con-eq :kools :yellow]
[:next-to :chesterfields :fox]

where the first of each vector would specify the kind of constraint. But that led me to an awkward implementation of a dispatch mechanism for those rules, so I decided to encode them as (quoted) function calls instead:

'(coloc :japan :parliament) ; 10
'(coloc :coffee :green) ; 12
'(next-to :chesterfield :fox) ; 5

so I can dispatch the constraining rule with a simple eval. This seems a lot more elegant and "lisp-y." However, each of these functions needs to access my domain data (named vars), and this data keeps changing as the program runs. I didn't want to blemish my rules by introducing an extra argument, so I wanted vars to be available to the eval'd functions via dynamic scoping.

I've now learned that dynamic scoping can be done using binding, but it also needs a declare.

Community
  • 1
  • 1
Carl Smotricz
  • 66,391
  • 18
  • 125
  • 167
  • 1
    Are you just kicking the tyres or really trying to implement something? If the latter, I'd like to know what you are trying to do that forced you to use such a design -- at first glance delays or plain old closures could do the trick. – cgrand Apr 04 '10 at 19:02
  • @cgrand: Thanks for your interest! I've added a second update to explain what I'm trying to do. This is meanwhile working for me but I'm certainly open to better suggestions! – Carl Smotricz Apr 05 '10 at 07:21
  • @cgrand: My post [Constraint Satisfaction Problem](http://stackoverflow.com/questions/2500504/constraint-satisfaction-problem) now has my complete solution. If you're interested, you can see `binding` in action there. I'm very open to constructive criticism on my amateurish approach. – Carl Smotricz Apr 05 '10 at 11:29

2 Answers2

5

Do you mean something like this?

(def foo '(bar))
(declare bar)

(binding [bar (fn [] (println "hello bar"))]
  (eval foo))

If yes, your problem reduces to this:

(let [foo 1]
  (eval 'foo))

This won't work, because eval does not evaluate in the lexical environment. You can get around that using vars:

(declare foo)

(binding [foo 1]
  (eval 'foo))

As far as that is concerned, Clojure seems to have similar semantics to CL, cf. the CLHS:

Evaluates form in the current dynamic environment and the null lexical environment.

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
danlei
  • 14,121
  • 5
  • 58
  • 82
  • Yep, seems to work. It's nice that there is an increasing number of people on SO who are conversant in Clojure. Thanks! – Carl Smotricz Apr 04 '10 at 18:54
  • You're welcome. I suppose this is for educational purposes. Otherwise, consider cgrand's comment to your question. – danlei Apr 04 '10 at 19:05
3

I think you're solving the wrong problem. In functional languages, functions are values and can be assigned to anything that can store any other value, e.g. a map. You shouldn't be trying to manipulate namespaces or evaling anything - this isn't perl.

Try something like this, and use assoc to change the map locally:

user=> (def fnmap {:funcA (fn [x] (inc x)), :funcB (fn [x] (* x 2))})
#'user/fnmap
user=> ((:funcA fnmap) 10)
11
user=> ((:funcB fnmap) 10)
20
Chris
  • 31
  • 1
  • Thanks! I'm happy to confirm that this is pretty much exactly what I'm doing. Coding of my meanwhile working solution can be found in my [Constraint Satisfaction Problem](http://stackoverflow.com/questions/2500504/constraint-satisfaction-problem) post. – Carl Smotricz Apr 06 '10 at 22:45