Clojure functions - return value calculated before the last statement

I have several tests written in clojure. This is a simple example:

(defn test1 [] (start-server) (run-pvt-and-expect "PVT-0") (stop-server) ) 

I would like to return the result of "run-pvt-and-expect", but after that I need to perform other functions. How can I do this in clojure in a functional way (without using constructs like "let")? Thanks.

Note. I read this question and its answers, but could not apply it to my case. In addition, the comment requested examples that were never given, so please consider this as an example ...

+4
source share
5 answers

You can do this using the finally block.

 (try (+ 2 3) (finally (+ 6 7))) 

gives a result of 5 not 13.

Here is the relevant page of the document. The highlighting part:

any expression of exprs will be appreciated for their side effects.

Another option is to write a macro like open-open to start and stop your server when returning the correct results. Although this will simply shift the problem to the way you write the macro, because it will need to either use the let construct or try finally build the above.

+7
source

Well, I don't know anything built-in that you could use (except let , which you don't need). But the beauty is that you could build it yourself.

One idea would be to create a “new” defn , where the keyword :return left of the expression means returning the value from this, not the last expression. The macro will be appropriate here because we will create a modified expression (defn ...) that requires invaluable code.

 (defmacro defn-return-middle [nm arg-vec & body-exprs] `(defn ~nm ~arg-vec ~(if (some #{:return} body-exprs) (let [[before _ [to-be-returned & after]] (partition-by #{:return} body-exprs)] `(let [ret# (do ~@before ~to-be-returned)] ~@after ret#)) body-exprs))) 

This extends something like:

 (defn-return-middle f [] a :return b c) 

Sort of:

 (defn f [] (let [ret (do ab)] c ret)) 

For example, now you can:

 (defn-return-middle blah [a] (+ 1 a) :return (+ 2 a) (println "does this work?")) 

And now in REPL:

 user> (blah 5) does this work? =>7 

(Yay!)

Or for your example, you now do:

 (defn-return-middle test1 [] (start-server) :return (run-pvt-and-expect "PVT-0") (stop-server)) 

True, the macro uses let , but it works by automating the let extension that you would have done if you wrote it manually each time. This means that now, using this macro, you will no longer unsubscribe let . In addition, this macro does not currently work on function definitions that declare several phenomena. But it would not be too difficult to change it to work for them.

+4
source

I think the general concept you are looking for is a K-combinator. You can see it in the Fogus library . You can use it as follows:

 (defn test2 [] (println "start server") ((K (identity :return-value)) (println "stop server"))) => (test2) start server stop server :return-value 

This seems too complicated for this case, so perhaps we can simplify it (I'm not sure if the anonymous function in this “officially” is a k-combinator or not):

 (defn test3 [] (println "start server") ((fn [ab] a) (identity :return-value) (println "stop server"))) => (test3) start server stop server :return-value 

Since literal vectors are not lazy, you can also use it in the same way first:

 (defn test4 [] (println "start server") (first [(identity :return-value) (println "stop server")])) => (test4) start server stop server :return-value 

Honestly, in real code, I would just use let (as you don't want to do), because the goal seems clearer to me:

 (defn test5 [] (println "start server") (let [result (identity :return-value)] (println "stop server") result)) => (test5) start server stop server :return-value 
+3
source

here is a “functional” way, without using let constructs:

 (defn apply-get-first [cf] (f) c) (defn p [] (start (apply-get-first (compute) #(end))) 

and now with let:

you can do it like this:

 (defn p[] (let [s (start) r (compute) e (end)] r)) 
+1
source

This is what you need! Just look for unnecessary complexity when trying to avoid this.

The situation is to have three side effects that should be performed when returning the result of the second. Typical use case: access to a database.

+1
source

Source: https://habr.com/ru/post/1487470/


All Articles