Domchi
Domchi

Reputation: 10813

How to return dereferenced value as a result of a function in Clojure

I have a atom with a vector value:

(def v (atom [1 2]))

I want to write a function that will return this dereferenced atom. That is, I basically want to write a function that will return the same result as this:

=> @v
[1 2]

My first take of this was something like this:

=> (let [f #(@v)]
     (println (f)))
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

But this of course doesn't work as dereferenced atom is not a function:

=> (@v)
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

The workaround I finally ended up with is this:

=> (let [f #(vec @v)]
     (println (f)))
[1 2]
nil

...but surely there must be a better way to do this that doesn't have to assume that value of atom is vector?

Upvotes: 2

Views: 876

Answers (4)

leetwinski
leetwinski

Reputation: 17859

first of all: the reason of this error, is the expansion of lambda expression sugar syntax. You could think of it this way:

#(expression*) => (fn [] (expression*))

so, thinking this way:

#(@v) => (fn [] (@v)) and @v in turn is expanded to (deref v) (easily seen with macroexpansion), that lead you to this form:

(fn [] ((deref v)))

In fact you are half right with the assumed reason of the error: the result of dereference is being called as a function, but in fact it could be (as vector has functional semantics), rather it just lacks a mandatory parameter (exactly what is said in the error text). so it should work for this: #(@v 0) => (fn [] ((deref v) 0))

user> (let [f #(@v 0)]
        (println (f)))
1
nil

personally i would just go with a partial application, instead lambda expression to solve this:

user> (let [f (partial deref v)]
        (println (f)))
[1 2]
nil

Upvotes: 1

Sam Estep
Sam Estep

Reputation: 13294

None of the existing answers give the two simplest, most concise solutions, so here they are. As stated by @Stefan, you want something equivalent to this function:

(fn [] (deref v))

However, this usage of deref is exactly the use case that the @ reader macro is meant to simplify:

(fn [] @v)

Or, since (deref v) is a function call, you could instead use the #() reader macro:

#(deref v)

The main point here is that you can't use both @ and #() together in this case. But that doesn't mean you can't or shouldn't use one of them.

Upvotes: 2

Michiel Borkent
Michiel Borkent

Reputation: 34800

I want to write a function that will return this dereferenced atom

(def v (atom [1 2]))
(defn foo [] @v)
(foo) ;;=> [1 2]

Upvotes: 1

Stefan Kamphausen
Stefan Kamphausen

Reputation: 1665

Sometimes it is better to leave reader constructs like # and @ out and just go with the functions (or macros and special forms):

user> (def v (atom [1 2]))
#'user/v
user> (let [f (fn [] (deref v))] (f))
[1 2]

I take it, that your example is a very simplified version of what you really want to do. If not, deref may be just the function you are looking for.

Upvotes: 1

Related Questions