1

I am trying to refactor this code to use if-let:

om/IWillMount
  (will-mount [_]
    (go (while true
          (if (om/get-state owner :is-loaded)
            (let [updated-world (<! (update-world (:dimensions opts) (:world @data)))]
              (om/transact! data #(assoc % :world updated-world))
              (swap! app-state assoc :world updated-world))
            (let [world (<! (get-world (:dimensions opts)))]
              (om/set-state! owner :is-loaded true)
              (om/transact! data #(assoc % :world world))
              (swap! app-state assoc :world world)))
          (<! (timeout (:poll-interval opts))))))

So far I have tried this:

om/IWillMount
  (will-mount [_]
    (go (while true
          (if-let [world (om/get-state owner :is-loaded)]
                    (<! (update-world (:dimensions opts) (:world @data)))
                    (<! (get-world (:dimensions opts)))
              (om/set-state! owner :is-loaded true)
              (om/transact! data #(assoc % :world world))
              (swap! app-state assoc :world world))
          (<! (timeout (:poll-interval opts))))))

But I get this error:

Caused by: java.lang.IllegalArgumentException: if-let requires 1 or 2 forms after binding vector in web-game-of-life.app:58

2 Answers 2

5

if-let is not applicable...

The if-let macro is not applicable here. In an if-let, the result of the binding form is used as the test. It condenses code like

(let [world (get-world ...)]
   (if world
     (do-something-with world)
     (do-something-else)))

if, for example, get-world were to return nil when there was no world retrieved.

In your case value you want to test is different than the value you want to bind. You are testing (om/get-state owner :is-loaded) but not otherwise using the result.

...but duplicate code can be factored out

Your code does have duplication though, so there is a factoring opportunity. First, the same code with the first binding symbol changed to match the second, with the duplicated lines marked.

          (if (om/get-state owner :is-loaded)
            (let [world (<! (update-world (:dimensions opts) (:world @data)))]
(1)           (om/transact! data #(assoc % :world world))
(2)           (swap! app-state assoc :world world))
            (let [world (<! (get-world (:dimensions opts)))]
              (om/set-state! owner :is-loaded true)
(1)           (om/transact! data #(assoc % :world world))
(2)           (swap! app-state assoc :world world)))
          

Now factored by inverting the if and the let

(let [world (if (om/get-state owner :is-loaded) 
              (<! (update-world (:dimensions opts) (:world @data)))
              (do
                (om/set-state! owner :is-loaded true) 
                (<! (get-world (:dimensions opts)))))] 
  (om/transact! data #(assoc % :world world)) 
  (swap! app-state assoc :world world))
Sign up to request clarification or add additional context in comments.

2 Comments

I ended up refactoring to this gist.github.com/dagda1/4c8034ea42ad23b2e2d6. Why is the do block necessary in your example?
@dagda1 Unlike when, if does not have an implicit do. So if you want more than one form for the then or else clauses, you have to be explicit.
2

if-let, like if expects that the expects either 2 or 3 parameters, where the second is evaluated if the condition is true, and the optional third is evaluated if the condition if false. You can use do to evaluate more than one statements in such cases.

1 Comment

In Addition: With do you can combine a whole block which shall be executed on the right place. For example: (do statement1 statement2 ...)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.