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 clojure@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+unsubscr...@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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to