Hamza Yerlikaya
Hamza Yerlikaya

Reputation: 49339

Function arguments multiple types

I am trying to delay various calculations. I have functions of the following form,

(defn a-fn [a b]
  (let [a (if (fn? a)
            a
            #(identity a))

        b (if (fn? b)
            b
            #(identity b))]
    (+ (a) (b))))

this allows me to pass a-fn, a value or a function that returns the value,

(a-fn 1 2)    
(defn x [] 1)    
(defn y [] 2)    
(a-fn x y)
(a-fn x 2)

what I do is build a list of functions (like the one above) to operate on some data, fns may use other fns to retrieve their arguments or in some cases things don't change and they are assigned values as arguments. I was wondering is there a better way to achive this kind of behavior?

Upvotes: 6

Views: 262

Answers (2)

Jouni K. Seppänen
Jouni K. Seppänen

Reputation: 44172

You can use delay and force:

user=> (defn a-fn [a b] (+ (force a) (force b)))
#'user/a-fn
user=> (a-fn 1 2)
3
user=> (def x (delay 1))
#'user/x
user=> (def y (delay 2))
#'user/y
user=> (a-fn x y)
3
user=> (a-fn x 2)
3

If you try something like (delay (prn :hello) 1) to test when the computation is done, note that printing the Delay object forces it; so (def x (delay ...)) is safe, but typing a plain (delay ...) in the REPL prompt is not.

Upvotes: 6

Dave Ray
Dave Ray

Reputation: 40005

There might be a more elegant way to do what you want, but here's at least a more generic version of it:

(defn delayed [reducer & fs]
  (apply reducer (for [f fs] (if (fn? f) (f) f))))

(def a-fn (partial delayed +))

So delayed takes an arbitrary function and a list of function/values. If expands all the args and then applies the function to them. Then we use partial to define your a-fn using +:

user=> (a-fn 1 2)
3
user=> (a-fn (constantly 1) 2)
3
user=> (a-fn (constantly 1) 2 4)
7

Alternatively, it might make sense for delayed to return a function rather than using partial. Note sure which is better.

A better name than "delayed" is welcome :)

Upvotes: 2

Related Questions