These are chat archives for ramda/ramda

12th
Dec 2015
Tobias Pflug
@gilligan
Dec 12 2015 10:58 UTC
Meh, just realized again that I have no idea/intuition about transducers - i need to fix that
Aldwin Vlasblom
@Avaq
Dec 12 2015 13:31 UTC

@gilligan You can view it as a way to compose a computation, where every step takes the value(s) from the previous step, and emits zero or more new values for the next step. This allows the final computation to be used in any kind of reduction (map, filter, etc) of any kind of list (array, stream, generator, etc). The internal mechanism also has a built-in way to deal with short-termination (when doing a take(), for example).

Since you're extending the "step" function you end up with a means to "stream" your input list through the entire computation value by value, rather than doing .map().filter().take() and building up intermediate lists in between steps. So once you've coded a transducer it is more efficient, and more reusable than when you code a chain of operations.

I found watching this video over and over again quite helpful in gaining an understanding: https://www.youtube.com/watch?v=6mTbuzafcII

Tobias Pflug
@gilligan
Dec 12 2015 15:04 UTC
Thanks, at the surface all that does make perfect sense. I just need to find (identify appropriate) opportunities to use transducers
@Avaq i think it boils down to me having to play around with transducers ;)
Aldwin Vlasblom
@Avaq
Dec 12 2015 15:11 UTC
@gilligan Just use them any time you have a sequence of steps, if only for the performance gain. Like instead of going: pipe([map(f), filter(g)], arr) go into([], compose(map(f), filter(g)), arr). I really like using them for processing object streams in Node with transduce-stream, because it allows me to work using my normal Ramda workflow on Streams:
import throughx from 'transduce-stream';
stream.pipe(throughx(compose(
  map(f),
  filter(g),
  take(5)
), {objectMode}))
Tobias Pflug
@gilligan
Dec 12 2015 15:14 UTC
@Avaq currently working in a big React project with ramda. Suppose I will just have a look at the various scenarios where I apply a transformation pipeline via R.pipe
Keith Alexander
@kwijibo
Dec 12 2015 15:21 UTC
@gilligan this blog post is what helped me grok transducers http://phuu.net/2014/08/31/csp-and-transducers.html
boxofrox
@boxofrox
Dec 12 2015 18:46 UTC
@Avaq, your pipe and compose from :point_up: December 12, 2015 10:11 AM have the same order of operations. I take it transducers do something to reverse the order of operations?
Raine Virta
@raine
Dec 12 2015 18:47 UTC
they don't exactly do anything, it's inherent to the way they compose
it took me a while to understand why it happens
boxofrox
@boxofrox
Dec 12 2015 18:52 UTC
Thanks, @raine. I'll dig around and see if can figure it out, too. I vaguely recall Rich Hickey's talk covered that composition bit. Think I'll start there.
Tobias Pflug
@gilligan
Dec 12 2015 19:07 UTC
Can someone merge ramda/ramda#1541 ? master is broken right now
boxofrox
@boxofrox
Dec 12 2015 20:15 UTC
Note to self, don't use Ramda functions in undocumented ways. In 0.17, invoker(1, 'getResponseHeader', 'content-type') worked fine, but was documented to be used as invoker(1, 'getResponseHeader')('content-type'). I suspect the currying of pipe and compose had something to do with that. Upgrade to 0.18 and I run into errors with functions further down the pipe because 'content-type' was effectively dropped. Doh.
Aldwin Vlasblom
@Avaq
Dec 12 2015 20:57 UTC
@boxofrox Imagine you're building up your "step" function by wrapping the previous step functions. That's what happens when you compose a transducer: compose(a, b, c) = (from right to left) "c is wrapped by b is wrapped by a", so you end up with a transformation that first applies a, and a then calls b and b then calls c.
So you compose the transducer right-to-left, but end up with a step function that executes "left to right", so to say.
Aldwin Vlasblom
@Avaq
Dec 12 2015 21:16 UTC

With a transducer you're really just extending a step function. Let's take into as an example. Without a transformation over the step function , all into([], identity, list) does is iterate list and append every item to []. So the step function here is append. However, I could choose to extend the step function, by not passing identity, but (a,b -> b) -> (a,b -> b) (note how the functions have the same signature as append). So the function I'm passing to into([], f, list) takes the step function as a first argument, and returns a new step which wraps the old one in order to apply some custom logic (like transforming its argument, in case of map). In the case of a composition of these "step transformers" that means we're just taking this step function, and threading it through the pipeline, decorating it along the way. In the end we're left with append, but wrapped many times in order to do all sorts of things with its argument before it's finally called (or not called, if you're using filter as one of its "decorators").

Now, that's a bit simplified, in reality our step function is actually three functions (contained in an object), one init (like "setup") one step and one result (like "teardown"), but the idea remains the same.

Tobias Pflug
@gilligan
Dec 12 2015 21:44 UTC
@Avaq so taking some random example, can I write https://gist.github.com/gilligan/75f1b2277f92c751a476 with a transducer instead ?
boxofrox
@boxofrox
Dec 12 2015 21:44 UTC
@Avaq, thanks for the explanation.
Tobias Pflug
@gilligan
Dec 12 2015 21:59 UTC
@Avaq guess not. quite simply because R.toPairs is not a transducer
Aldwin Vlasblom
@Avaq
Dec 12 2015 22:01 UTC
@gilligan I believe the mapKeys function from your example takes in an object as its argument (http://goo.gl/qGer0u). Transducers are really only useful if you're transforming multiple iterations of data the same way. There is a function inside your example which does just that (adjustKeyPrefixes), so you could rewrite that to be a transducer. However, it wouldn't add much since it's only a single step: map. :)
Tobias Pflug
@gilligan
Dec 12 2015 22:04 UTC
@Avaq yeah i suppose it's interesting when you are streaming data in some way - not really doing that at the moment hehe. I am still interested to learn about it though. will spend some time reading the (readily available) material
Aldwin Vlasblom
@Avaq
Dec 12 2015 22:11 UTC
It's certainly nice for streaming, because transducers act upon single values rather than entire collections at once, so you don't need anything buffered up to use a transducer on it. However, you could just as well use it on buffered collections like Arrays. And I can't imagine many useful programs that don't transform values in lists at some point: Lists of users, lists of posts, lists of click-events (if you're doing reactive), etc. I'm sure you'll run into a list that you have to map over or filter, or slice. And when you're doing multiple of those kinds of operations in sequence on the same list, a transducer is probably better at it. :)
Tobias Pflug
@gilligan
Dec 12 2015 22:17 UTC
sure, plenty ;}
Tobias Pflug
@gilligan
Dec 12 2015 22:40 UTC
hm, i cam agonising about how this could be written in a more efficiently : https://gist.github.com/gilligan/dc1409dee420798c73dc ( @Avaq you see i extracted that one function from there). I have modify keys and values of an object and currently this happens in two successive traversals which I already don't like but don't see right now how to avoid this. Any ideas anyone ? ;-)