Ryan Asensio
Ryan Asensio

Reputation: 93

Return value from middle of `do` (Clojure)

I have a list of I/O functions to run in a game, but need to collect the value from a function somewhere in the middle of the do

(defn setup-steps [game-state]
  (do (io/clear-screen)
      (print-welcome-message)
      (initial-setup) ;; value to be collected
      (io/clear-screen)
      (io/print-board game-state)))

Is there a smart way to return the value from somewhere in the middle of a do?

Down the line, I am using the return value of setup-steps to update an atom, like so:

(defn game-loop [game]
  (while (:game-in-progress? @game)

     ;; Here is where I am using the results
    (->> (s-io/setup-steps @game) (state/updater game))

    (while (:game-in-progress? @game)
      (->> (m-io/turn-steps @game) (state/updater game)))
    (->> (eg-io/end-game-steps @game) (state/updater game)))
  (eg-io/exit-game))

Where

(defn updater
  "Updates the game state.
  This is the only place where the game atom is modified."
  [game update-params]
  (swap! game merge update-params))

I'm sure you could write a macro for this, but I don't really understand macros yet.

And maybe I am thinking about this the wrong way... Is it more idiomatic to dow the swap!ing inside setup-steps?

Upvotes: 1

Views: 182

Answers (2)

amalloy
amalloy

Reputation: 91857

Fundamentally the only way to do this is with let, as clartaq shows. But if you find this distasteful there are a number of ways you could wrap this up in a macro to make it prettier to look at and also more clear about what you're doing. Here is the simplest one, which I like to call returning:

(defmacro returning [x & more]
  `(let [x# ~x]
     (do ~@more)
     x#))

(defn foo []
  (x)
  (y)
  (returning (z)
    (a)
    (b)))

The do in returning is of course superfluous, but I think it's still useful to emphasize that more is evaluated only for side effects.

Upvotes: 3

clartaq
clartaq

Reputation: 5372

Any reason you can't assign the result in a let and return it at the end of the function?

(defn setup-steps [game-state]
  (io/clear-screen)
  (print-welcome-message)
  (let [v (initial-setup)] ;; value to be collected
    (io/clear-screen)
    (io/print-board game-state)
    v))

EDIT: Got rid of the redundant do that Ryan Asensio mentioned.

Upvotes: 7

Related Questions