Live data from Hacker News

Transducers are coming to Clojure

blog.cognitect.com

81–90 of 103 posts

Re: Transducers are coming to Clojure

#81

Tentative benchmark results have surfaced: https://github.com/thheller/transduce-bench Add salt according to taste.

The benchmark is wrong atm, the compared functions do not yield the same result.

(comp (map inc) (filter even?)) means filtering first, then incrementing.

The opposite for (->> data (map inc) (filter even?) ...

- which is not the same. And of course, it also means that the latter has to increment the double amount of numbers.

EDIT: It was me who was wrong, thanks for the corrections. Pitfall successfully identified :)

Re: Transducers are coming to Clojure

#82

Tentative benchmark results have surfaced: https://github.com/thheller/transduce-bench Add salt according to taste.

The benchmark is wrong atm, the compared functions do not yield the same result. (comp (map inc) (filter even?)) means filtering first, then incrementing. The opposite for (->> data (map inc) (filter even?) ... - which is not the same. And of course, it also means that the latter has to increment the double amount of numbers. EDIT: It was me who was wrong, thanks for the corrections. Pitfall successfully identified :…

Nope, that's the funny feature of transducers. Like lenses in Haskell, transducers compose in reversed order.

    (sequence (comp (map inc) (filter even?)) (range 10))
    ;;=> (2 4 6 8 10)
    (->> (range 10) (map inc) (filter even?))
    ;;=> (2 4 6 8 10)
Both test functions in transduce-bench return 250000500000.

Re: Transducers are coming to Clojure

#83

Tentative benchmark results have surfaced: https://github.com/thheller/transduce-bench Add salt according to taste.

The benchmark is wrong atm, the compared functions do not yield the same result. (comp (map inc) (filter even?)) means filtering first, then incrementing. The opposite for (->> data (map inc) (filter even?) ... - which is not the same. And of course, it also means that the latter has to increment the double amount of numbers. EDIT: It was me who was wrong, thanks for the corrections. Pitfall successfully identified :…

It seems counterintuitive, but composing transducers yields a reducing function that runs the transformation steps left->right. What you are composing is the reducing function transformations (right to left, ordinary composition), but their result, having been built inside-out, runs the steps outside-in. So (comp tx1 tx2...) runs in same order as (->> xs tx1 tx2...)

Re: Transducers are coming to Clojure

#84

As someone who tried Clojure and failed, serious question: Does anyone actually use all these crazy features/patterns that keep getting added/discovered and talked about? I ask because even though I can imagine someone smart mastering these things and programming faster, I can't imagine a second person being able to understand his code, maintain it, and generally be productive. I imagine the second person losing a lo…

I work in a clojure shop with about 10 other developers. Until very recently, we hired folks with zero exposure to clojure or even function programming. Do we use all the new bells and whistles that Rich and the folks at Cognitect develop for clojure? Yes, but we're not jumping into the existing code base and refactoring everything to use core.async or reducers. Usually, one or two guys will use it in a less public, less critical piece of the infrastructure as a real-world application and then we do a code review with the whole team.

So, how do you build a clojure team, exactly? Don't try to exclusively hire developers who have been exposed to it yet or have masters degrees from elite universities, focus on finding people who love to program, who are genuinely interested in improving their own abilities, but can clearly hold their own in the language they currently use. You will soon have a team of developers who love the challenge of keeping up with what guys like Rich Hickey (and Cognitect) are doing. We have been very successful with this.

Re: Transducers are coming to Clojure

#85
post #42

Earlier quoted context omitted.

They are more comparable to Oleg's Enumerators ( http://okmij.org/ftp/Haskell/Iteratee/describe.pdf ), in that you compose a series of computations and then push data through them. The type signature is similar: type Iteratee el m a -- a processor of 'els' in the monad 'm' returning a type 'a' type Enumerator el m a = Iteratee el m a -> m (Iteratee el m a) The Enumerators library is complicated by the presence of mon…

While not mentioned in the blog post, the transducers implementation supports both early termination and result completion/flushing/cleanup.

there does seem to be some overlap with stream fusion -- which was all about exploiting the optimization opportunities when separating the collection operation from the kernel performed on each element.

We called the bits in the middle "step functions" , which could be combined with "consumers", "producers" and "transformers".

And the algebra generalizes hugely (not just collections) but to things like concurrent processes, data flow programs etc.

http://metagraph.org/papers/stream_fusion.pdf

Things to think about in a non-Haskell settings: how do you prevent reordering side-effects? Can execeptions/non-termination being reordered be observed?

Re: Transducers are coming to Clojure

#86
post #62
post #45

I'm not quite sure what this means, so here's my attempt to translate this into Python. A reducer is a function such as `add`: def add(sum, num): return sum + num Of course you can plug `add` directly in `reduce(add, [1, 2, 3], 0)` which gives `6`. A transducer is an object returned by a call such as `map(lambda x: x + 1)`. You can now apply the transducer to a reducer and get another reducer. map_inc = map(lambda x:…

If I understand right (I may not): In Python, the (complex, generic) iter() protocol is how every container provides a method to iterate itself, and reduce is a trivial application of a reducer (like your add function) to a container by using the iter protocol. In Clojure, it's the opposite: there is a reduce() protocol and every reducible container knows how to reduce itself. the traditional reduce function just tak…

Excellent answer.

Re: Transducers are coming to Clojure

#87
post #71
post #70

Earlier quoted context omitted.

Interesting. Continuing to try to analyze these in Haskell, I think this is a direct translation: -- z is just there to not clobber some standard prelude names type Red r a = r -> a -> r zmap :: (b -> a) -> Red r a -> Red r b zmap f f1 result input = f1 result (f input) zfilt :: (a -> Bool) -> Red r a -> Red r a zfilt p f1 result input = if p input then f1 result input else result ztake :: Int -> Red r a -> (r -> a -…

Also, you can get rid of the sentinel value by packing along a termination continuation value as well: type Red r a = (r -> a -> r, r) zmap :: (b -> a) -> Red r a -> Red r b zmap f (f1, z1) = (\result input -> f1 result (f input), z1) zfilt :: (a -> Bool) -> Red r a -> Red r a zfilt p (f1, z1) = (\result input -> if p input then f1 result input else result, z1) ztake :: Int -> Red r a -> Red r a ztake n (f1, z1) = (r…

Doesn't ztake need to live in State Int (or something similar)? As written, I don't see how it passes the 'n' onto the next call. It seems that the transducer returned by ztake n for any n > 1 will always pass on (f1 result, z1).

Thank you for posting this though, helpful to see someone work through it.

Re: Transducers are coming to Clojure

#88
post #87
post #71

Earlier quoted context omitted.

Also, you can get rid of the sentinel value by packing along a termination continuation value as well: type Red r a = (r -> a -> r, r) zmap :: (b -> a) -> Red r a -> Red r b zmap f (f1, z1) = (\result input -> f1 result (f input), z1) zfilt :: (a -> Bool) -> Red r a -> Red r a zfilt p (f1, z1) = (\result input -> if p input then f1 result input else result, z1) ztake :: Int -> Red r a -> Red r a ztake n (f1, z1) = (r…

Doesn't ztake need to live in State Int (or something similar)? As written, I don't see how it passes the 'n' onto the next call. It seems that the transducer returned by ztake n for any n > 1 will always pass on (f1 result, z1). Thank you for posting this though, helpful to see someone work through it.

Yeah, I'm working through it in more depth now (I just fired this off last night quickly without a lot of thought). There's more to be said about take. My implementation here totally does not work.

Re: Transducers are coming to Clojure

#89

As someone who tried Clojure and failed, serious question: Does anyone actually use all these crazy features/patterns that keep getting added/discovered and talked about? I ask because even though I can imagine someone smart mastering these things and programming faster, I can't imagine a second person being able to understand his code, maintain it, and generally be productive. I imagine the second person losing a lo…

I work in a clojure shop with about 10 other developers. Until very recently, we hired folks with zero exposure to clojure or even function programming. Do we use all the new bells and whistles that Rich and the folks at Cognitect develop for clojure? Yes, but we're not jumping into the existing code base and refactoring everything to use core.async or reducers. Usually, one or two guys will use it in a less public,…

Sounds like the job I'm looking for :)

Re: Transducers are coming to Clojure

#90
post #62
post #45

I'm not quite sure what this means, so here's my attempt to translate this into Python. A reducer is a function such as `add`: def add(sum, num): return sum + num Of course you can plug `add` directly in `reduce(add, [1, 2, 3], 0)` which gives `6`. A transducer is an object returned by a call such as `map(lambda x: x + 1)`. You can now apply the transducer to a reducer and get another reducer. map_inc = map(lambda x:…

If I understand right (I may not): In Python, the (complex, generic) iter() protocol is how every container provides a method to iterate itself, and reduce is a trivial application of a reducer (like your add function) to a container by using the iter protocol. In Clojure, it's the opposite: there is a reduce() protocol and every reducible container knows how to reduce itself. the traditional reduce function just tak…

my heart somehow leaps when I see beautiful code like this.

Although I have tried to like clojure in all earnestness, I am somehow put off by the noise from clojure ( PS its not only about the brackets. )

Somehow the python code and also the haskell version looks so succinct yet sparse enough to be read.

maybe I am hardwired that way ...

Post reply on HN