user3397554
user3397554

Reputation: 23

How to execute "do" on elements in a sequence in clojure

I've been trying to figure out how to execute expressions that are stored as elements in a sequence. For example, here are two expressions in a sequence:

user=> (def z '((println 'x) 'y))
#'user/z
user=> z
((println (quote x)) (quote y))

Trying to use do or doall on them doesn’t seem to do anything

user=> (do z)
((println (quote x)) (quote y))
user=> (doall z)
((println (quote x)) (quote y))

The output I am trying to get is if I were to execute them not as a sequence but as a list of arguments

user=> (do (println (quote x)) (quote y))
x
y

I tried mapping eval but that gives me an extra nil and returns a list

user=> (map eval z)
(x
nil y)

Any help would be greatly appreciated!

Upvotes: 1

Views: 131

Answers (3)

deadghost
deadghost

Reputation: 5237

(def z '((println 'x) 'y))

Your z definition has a quote saying DON'T EVALUATE ME. So z is defined as an unevaluated expression. You want to evaluate unevaluated code so naturally you'll want to use eval; however (eval z) won't work because (println x) will print x but return the value nil. (nil y) will not work and isn't what you want.

What you do want is do; do only returns the value of the last expression so used here nil does not get in the way and you execute all your expressions. You want your end result to be:

(do (println 'x)
    'y))

Let's transform z to look like that.

(cons 'do z)
=> (do (println (quote x)) (quote y))

Looking familiar? eval it!

(eval (cons 'do z))
=> x
   y

Upvotes: 1

BillRobertson42
BillRobertson42

Reputation: 12883

You're looking to eval code here.

user=> (def z '(do (println "hey!" 'x) 'y))
#'user/z
user=> (eval z)
hey! x
y

Note that I added the do so that both expressions would be evaluated. The code in the question contained an error.

((println 'x) 'y)

First thing that would occur would be to print 'x and println returns nil, so the expression would then look like this.

(nil 'y)

nil is not a function, but it's being evaluated at the head of the list, so that's an error. Or a null pointer exception in this case.

user=> (def z '((println 'x) 'y))
#'user/z
user=> (eval z)
x
NullPointerException   user/eval674 (NO_SOURCE_FILE:1)

Upvotes: 1

noisesmith
noisesmith

Reputation: 20194

Do is a macro that evaluates its args, and returns the last one. let, fn, for among others contain implicit do bodies.

In your map example, the "extra nil" is the return value of the println, and it is actually the x that is extra (it is the printed output interleaved with the sequence that map returns).

Your definition of z does not create executable objects, but lists. Lists just evaluate to themselves when used.

Do you need to evaluate expressions stored as literals?

It is easy to wrap each each one as a thunk (a function of no arguments).

user> (def z [#(println 'x) #(do 'y)])
#'user/z

We can use reduce to execute them all, and only return the last one.

user> (reduce (fn [_ e] (e)) nil z)
x
y

here x is printed, and y is returned.

If you really need to use a list with eval, you can substitute it as apropriate above ((e) becomes (eval e) and z gets your original definition).

Upvotes: 3

Related Questions