Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I absolutely love this question. Maybe I'm surprised that to see a novel question that is so simple. Anyways, drooling aside, I <em>think</em> I have something that would work for you.</p> <p>Instead of doing:</p> <pre><code>(if (or @future1 @future2) ...) </code></pre> <p>Do:</p> <pre><code>(if (or (and (realized? future1) @future1) (and (realized? future2) @future2)) ...) </code></pre> <p>The trick is testing whether something is realized before asking whether it is <code>true</code> or <code>false</code>.</p> <p>This could be generalized to a macro like so:</p> <pre><code>(defmacro future-or [&amp; futures] `(or ~@(for [f futures] `(and (realized? ~f) (deref ~f))))) </code></pre> <p>And then used like:</p> <pre><code>(if (future-or future1 future2) ...) </code></pre> <p>Wait a second. Maybe I'm understanding your problem incorrectly. Perhaps you want to block execution until EITHER one of the futures is done and returns <code>true</code>, in which case you do the then clause of your <code>if</code>, OR both of your futures are done and neither returns <code>true</code>, in which case you do the else clause of your <code>if</code>. That's a different story.</p> <p>The most succint way I could come up with isn't exactly pretty, but it's not hideously long either:</p> <pre><code>(if (loop [] (cond (or (and (realized? future1) @future1) (and (realized? future2) @future2)) true (and (realized? future1) (realized? future2) (not @future1) (not @future2)) false :else (recur))) ...) </code></pre> <p>Now, this uses <code>loop</code> to repeatedly loop until one of two things happen: either one of the futures is both realized and <code>true</code>, in which case, the loop returns with <code>true</code>; or all of the futures are realized and all of them <code>false</code>, in which case, the loop returns with <code>false</code>. It's important to have the <code>(not ...)</code> expressions at the end of their parent <code>(and ...)</code> expression, so that you don't get stuck checking whether any futures are <code>true</code> or <code>false</code> until they are all realized.</p> <p>This new solution could be generalized as:</p> <pre><code>(defmacro future-or [&amp; futures] `(loop [] (cond (or ~@(for [f futures] `(and (realized? ~f) (deref ~f)))) true (and ~@(for [f futures] `(realized? ~f)) ~@(for [f futures] `(not (deref ~f)))) false :else (recur)))) </code></pre> <p>And used in the same way as the above <code>future-or</code> example.</p> <p>Now, I know next to nothing about optimization. But as far as I can tell, this certainly isn't as efficient as it theoretically could be, because once any given future is realized, there is no real need to test its value more than once. Well, here are two solutions, which I've titled <code>future-some</code>. Since the futures being tested has to potentially change dynamically after every loop iteration, I had to make it a function. In that way, this new method is analogous to <code>some</code>, not <code>or</code>. In kind, I changed the behavior to take a collection of futures (as opposed a variable number of single arguments--another difference between <code>some</code> and <code>or</code>). One solution does no double-checking (I think):</p> <pre><code>(defn future-some [futures] (if (empty? futures) false (let [res (reduce (fn [unrealized f] (if (realized? f) (if @f (reduced true) unrealized) (cons f unrealized))) () futures)] (if (true? res) true (recur res))))) </code></pre> <p>There's a lot to detail here, but the gist is this: if there are no futures to test, it returns <code>false</code>, otherwise, it iterates down the list of futures, testing whether any of them are realized. If a future is realized, and also dereferences to <code>true</code>, the iteration breaks to return <code>true</code>. If a future is realized but does not dereference to <code>true</code>, then the iteration continues to the next item. If a future is unrealized, it is added a list to be used in the next recursion of <code>future-some</code>.</p> <p>And the other solution is more concise, but somewhat less optimal:</p> <pre><code>(defn future-some [futures] (if (empty? futures) false (let [realized (filter realized? futures)] (if (some deref realized) true (recur (remove (set realized) futures)))))) </code></pre> <p>Similar to the other one, except that it filters out the realized first, then tests, then filters again (this time inverse--to get the unrealized) if it needs to recur. This second filtering is the inefficient step.</p> <p>A problem with all the solutions I propose are that future cancellations would result in errors upon dereferencing, when they should probably simply go on as though that future were false. This is solvable by placing <code>(not (future-cancelled? ...))</code> expressions inside every single <code>(and ...)</code> expression, prior to any dereferencing. Or, for the <code>future-some</code> functions, you'd have to substitue the <code>realized?</code> predicate with <code>(some-fn (comp not future-cancelled?) realized?)</code>, or <code>#(and (not (future-cancelled %)) (realized? %))</code> for the faint of heart.</p> <p>Again, seriously, thank you for that question.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload