What sorcery is this? (`reduced`, `reduced?`)

221 views
Skip to first unread message

John Gabriele

unread,
Apr 25, 2017, 3:34:05 PM4/25/17
to Clojure
Just recently stumbled upon `reduced` and `reduced?`. It seems rather magical... how does the outer `reduce` (or `reductions`) know to stop? That is, how does the function which is being called (`reduced`) affect the function that's calling it (below, `reductions`)? :

~~~clojure
(defn main
  []
  (let [res (reductions (fn [accum x]
                          (if (< accum 100)
                            (+ accum x)
                            (reduced [x accum])))
                        (range))]
    (prn res)
    (prn (reduced? res)) ;=> false
    (prn (reduced? (last res))))) ;=> false --- why isn't this `true`?
~~~

Also, what's the use of `reduced?`, and on what in the above snippet would I call it to return true?

lvh

unread,
Apr 25, 2017, 3:54:19 PM4/25/17
to clo...@googlegroups.com
Hi John,


reduced really just wraps a value in a special container; nothing magical. All it does is signal to reduce to stop. reduce knows to stop by checking with reduced?. reduced? is otherwise extremely narrow-use: it's basically only useful if you're trying to write something like reduce but different yourself. 


lvh

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Luke Burton

unread,
Apr 25, 2017, 5:04:09 PM4/25/17
to clo...@googlegroups.com

To add to Ivh's answer, the source code isn't too hard to follow here. Cursive makes this really easy because you can follow the source right into clojure.lang.RT, but you don't need to go that far to see how `reduced?` is used.

You can see that all implementations of `reduce` in clojure.core.protocols call `reduced?` to see if they should stop reducing. So, this is a convention that a reducing or transducing process needs to follow. If you ever need to write a function that takes a transducer, and which applies that transducer without simply calling `transduce` or friends, you'll also need to uphold that contract. More here.

I thought I had found a nice application for `reduced?` outside this context, but alas it was not to be. I wanted to detect whether a `reduce` had terminated due to a condition being met or because it had exhausted the input collection, assuming that `reduced?` was checking for some metadata glued onto the value.

(reduced? (reduce #(if (= %2 5) (reduced %2) (+ %1 %2)) (range 1 10)))
=> false

The reason is that `reduced` wraps the value in a Reduced, which implements IDeref, and one must dereference it to obtain the wrapped value. The reducing / transducing processes not only must check for `reduced?` but they are also obliged to deref any such value that returns true for that check.

(reduced 5)
=> #object[clojure.lang.Reduced 0x471e4b70 {:status :ready, :val 5}]
(deref *1)
=> 5

So you really will never make use of `reduced?` unless you're *inside* an implementation of a reducing / transducing process. As Ivh already stated, extremely narrow-use :)


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Alex Miller

unread,
Apr 25, 2017, 5:11:59 PM4/25/17
to Clojure
The source for both functions is pretty simple (use the source!), although admittedly `reduced?` does some work to optimize perf that obscures it a little.

`reduced` is used by a reducing function to indicate to the outer reducing process that a return value should end the reduce. It does this by simply wrapping the return value in a Reduced object.

`reduced?` is used by the outer process to detect that wrapper (it just checks if it is a Reduced)

John Gabriele

unread,
Apr 26, 2017, 1:19:59 AM4/26/17
to Clojure
Thanks, all!

Luke, in `(source reduce)`, that code makes use of clojure.core.protocols/coll-reduce, which I see is in src/clj/clojure/core/protocols.clj. And although I haven't yet made sense of all that, I see that `coll-reduce` calls `seq-reduce`/`iter-reduce`, which in turn call `reduced?`!

Regarding `(source reduced)`, that points me toward clojure.lang.Reduced. And `(source reduced?)` points me toward clojure.lang.RT/isReduced.

I found the corresponding source code in

src/jvm/clojure/Reduced.java
src/jvm/clojure/RT.java

-- John
Reply all
Reply to author
Forward
0 new messages