On Friday, October 17, 2014 2:58:20 AM UTC-4, Michael van Acken wrote:
>
> Am Freitag, 17. Oktober 2014 04:02:52 UTC+2 schrieb Daniel James:
>>
>> Hi Michael,
>>
>> I’m glad you are in favor of this change; however, and with tongue firmly
>> in cheek, you’ve taken a beautiful thing and corrupted it, which I can’t
>> condone. ;)
>>
>> Let me offer an explanation as to why I half-jokingly say this.
>>
>> From what you’ve said, your “group-by-style transducer” is going to have
>> the following form:
>>
>> (fn [rf]
>> ([] {})
>> ...)
>>
>> Irrespective of the rest of its implementation, it’s a function that
>> transforms a reducing function into another reducing function, but it is
>> most certainly *not* a transducer—a necessary, but not a sufficient
>> condition.
>>
>> Before I make an appeal to types, let me build some intuition by
>> borrowing the analogy that Rich Hickey made in his talk at Strange Loop. He
>> was saying that he wanted to give instructions to the baggage handlers
>> about how to handle the baggage, without having to say anything about
>> whether the baggage arrived on a trolley or by conveyor belt. By returning
>> {} in the 0-arity, you are effectively saying, “No, it’s definitely going
>> to be on a trolley!”.
>>
>
> Hi Daniel,
>
> I'm not quite sure if your trolley-analogy is viable here (but see
> below). Please note that I'm working
> within a reduce scenario: a "conveyor belt" is moving data to the
> reducing function, and once the
> conveyor stops a single value is left behind -- the final result of the
> reducing fn.
>
> The grouping I am using does two things within this metaphor: in the one
> direction, it splits one conveyor
> into n independent conveyors; in the other direction, it combines n
> reduced results again into a single result.
>
> In code it looks like this:
>
> (defn group-by-xf [f]
> (fn [rf]
> (let [init (rf)]
> (fn
> ([] {})
> ([result]
> (reduce-kv (fn [acc k v] (assoc acc k (rf v)))
> {} result))
> ([result input]
> (let [k (f input)
> group-result (get result k init)]
> (assoc result k (rf group-result input))))))))
>
>
>
Hi Michael,
As I said above, while this is certainly a function that transforms one
reducing function into another, you should not call this a transducer. It
is something else, with a distinct type.
Compare
type Transducer a b = forall r . Reducer a r -> Reducer b r
with
type SomethingElse a b r = Reducer a r -> Reducer b PersistentMap
The baggage handler analogy is getting at the idea that you really
shouldn’t have to know anything about the conveyance, the `r` here. The
rank-2 polymorphic type neatly enforces that a valid implementation *can’t*
know. Your function does know, because it has removed the polymorphism and
always reduces on maps—you’ve dictated that you are using map trolleys.
>
>>
>> If I call
>>
>> (into [] xform (range 5))
>>
>> (transduce xform + 0 (range 5))
>>
>> I expect a vector and an integer to pop out, respectively, and I’m going
>> to be sorely disappointed if a map pops out instead!
>>
>>
> In this case you might prefer to write something like (transduce xform
> (comp group-by-xf +) ...) instead
> of (transduce (comp xform group-by-xf) + ...) . If I had used the first
> variant, then I would not have stumbled
> over the default init value of transduce at all. Mh. Seen this way, I
> only have to adjust my code accordingly and can
> continue to use the default init as it is. Thank you for this insight!
>
I think you may have missed my point here.
Your group-by-xf function only makes sense in a specific interpretation of
`transduce`, again indicating that it is not a valid transducer.
If `into` was changed to reflect my proposal, you would find it would blown
up with your group-by-xf:
(into [1 2 3] (group-by-xf odd?) (range 100))
A map would be returned not a vector! Similarly, if you used it with
`sequence`, you’d get a map, not a sequence; or with core.async channels,
you’d corrupt the channel by replacing its buffer with a map.
> By the way, please, please, don’t read this as a polemic. I just want to
>> be precise about the issue, and I am certainly sympathetic to “While
>> experimenting I…”!
>>
>
> No offense taken. I do understand that beauty is always in the eye of the
> beholder.
>
> Using the grouping function above I was able to take a gnarly aggregation
> function (basically a multi
> level update-in plus a transformation on the leaves when done) and turn it
> into a sequence of
> group-by-xf plus a much simpler reducing fn. This is very beautiful to me
> ;-)
>
> -- Michael
>
I agree that your group-by-xf is a nice formulation of a ‘group by’
operation. But I maintain that it should not be considered a valid
transducer. It should only be used with `reduce`.
I hope that helps with making my point clear.
Dan
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.