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.