I am trying to understand what are transducers - part 2
Tags: javascript
Published: 2023-10-19 22:07:13

In this post I continue my exploration on transducers, the following code resume what we have seen so far :

```js
const C = [1,2,4,5,6]

const concat = (a, b) => a.concat(b)

const mapReduce = (func, aggregator) => 
  (acc, current) =>  aggregator(acc, func(current))

const filterReduce  = (predicate, aggregator) => 
  (acc, current) => predicate(current) && aggregator(acc, current) || acc;

// applications
const mulBy5 = x => x * 5;
const isEven =  x => x % 2 == 1;
const mul7 = x => x * 7;
const add3 = x => x + 3

const oddReducer = filterReduce(isEven, concat)
const mulBy5Reducer = mapReduce(mulBy5, concat)
const add3reducer =  mapReduce(add3, concat)

```
So, we have expressed **map** and **filter** in terms of **reduce**

That allows to us to write the following code :

```js

let transformedC = C
  .reduce(mulBy5Reducer, [])
  .reduce(add3reducer, [])

``` 
Here we still create intermediate collections, nothing has changed to resolve the problem.

At this point it's time do read the definition of function composition :

**In mathematics, function composition is an operation ∘ that takes two functions f and g, and produces a function h = g ∘ f such that h(x) = g(f(x))**

Sadly, reducers (ie : mulBy5Reducer, add3reducer) are not composable.

The way I'm trying to solve the problem is purely experimental, and didn't lead to the 'canonical' solution, this will be for a next post.

Indeed, in the previous post I tried to use the 'transducer' notion, but I still don't get it in it's entirety

But anyway, let's try another path.

In our code we actually have 'composable' functions, those are the functions (applications) used in our reducers, ie : **mulBy5, isEven...**

So here are the steps I followed :

Starting from one reducer, for exemple :

```js
const mulBy5Reducer = mapReduce(mulBy5, concat);

```

The question is the following : how can we 'extract' the application **mulby5** in order to compose it ?

And that leads to another question, how can we make **mulBy5Reducer ** the result of a function call.

Let's call this function **T** for now.

So what we want is :

```js
let chainedTransformation = C
  .reduce(T(), [])

```
with :

```js

T = () => mulBy5Reducer

```
Let's continue our transformations:

```js

T = () => mapReduce(mulBy5, concat);
T = mapReduce => (mulBy5, concat);
T = mapReduce => mulBy5 => concat;

```

Note that I have isolated **mulBy5**.

We can then write our transformation like this :

```js

let chainedTransformation = C
  .reduce(T(mapReduce)(mulBy5)(concat),[])
  .reduce(T(mapReduce)(add3)(concat),[])
```
It's now time to see how we can compose our applications;

```js

  C.reduce (
    T(mapReduce)
      (compose 
        (
          mulBy5, 
          add3
        )
      )
  (concat), [])


 ```

Here the final code : [Demo](https://flems.io/#0=N4IgtglgJlA2CmIBcA2AHAOgCxoDQgDMIEBnZAbVADsBDMRJEDACwBcxYR8BjAeytbwByECAC+uanQZMAVmR79BwxgHpVAAgDiEAG5CNNDX1gJurCPw0ByAMLWAOlT5USrDbY0BeDeQDsuACsuADMALoA3E5OLm7G-Nw07j4AFDS4GgBGAJTeAHyGGC6JrCk5UVQI7nxgAA68JPDeGikYbQSuuV4FKRBUEBY0sABqQ10FHSQYAE7wUACu3PAAShAA5mwpKbpDGR3jGh3bYxl9AxBDo7DZFTH8cWA0tctzi02pBPPOGhk0a2uzNZJXjTA4tGjcbgZbjzaazARgv4A+BA1ggtKQvZfbgpGFwoSsbLZO6udxEWCCaYvBZLDTNFK1WZQCAleC-f6A4Gg-LgzHGWHwwk8xlzFlJeC4gUE3IAMhlhg5KK5GKheMFuQAPhrDJDbs57u4aDBAs0AB4880Aag0gQqsXcEBIAFF9FRmhpzd0PRoAKQaABM3h8AEY7QaNGB5rA-GaLRoAFQaPx6qoaAAq9NY0xorgIIMeFn4ByZb2mPLOyQKWZzJDz0wLlioKRLS2mp36hIidOi+tJGig8D42cEAAUmWLBO6RczWXGvdOJxLTbL5eatb4whoe-bDDBA6ll3Hrf69TunrUavVGlBmpeGk0Uk46c-nwOh+Kx6LWb1na7srgnxfZ8jSgYItzdIC6RAsDAMgkD-Vg59iSobdw2rXN8ySRtmlsGZXiWFI0xSR5nnw+BsjSWoL14Op7ygCjiiSf8N2QkkSF4BAMFgXg1hSdDa0wwsqGQ7s3R7cSUMkrgQEaMwhLIRh-QATiQf0-AAWgABiQTTxEkEBaHoEQihIBQQBcZRWBEcQwjEIA)  

```js

// Given a collection 'C'
const C = [7,5,3];

const concat = (a, b) => a.concat(b);
let compose = (...fns) => (initialVal) => fns.reduceRight((val, fn) => fn(val), initialVal);

const mapReduce = (func , aggregator) =>  (acc, current) =>  aggregator(acc, func(current))
const filterReduce  = (predicate, aggregator) => (acc, current) => predicate(current) && aggregator(acc,current) || acc;

const add5 = x => x + 5;
const isEven =  x => x % 2 == 1;
const mul7 = x => x * 7;

let T = (transformation) => reducer => init => transformation(reducer, init);  

const decoratePredicate =  predicate => x => predicate(x) && x || [] 

const add2 = (x) => x + 2;

const appcomposed = compose (
        decoratePredicate(isEven),
        add5, 
        add5, 
        add2
      )


const transformation = C.reduce(T(mapReduce)(appcomposed)(concat), [])

console.log(transformation)

 ```
I had to decorate the filter **isEven** in order to not return a boolean.

Note that instead of **compose** you can use **pipe** operator.
It's perhaps easier to read : [DemoWithPipe](https://flems.io/#0=N4IgtglgJlA2CmIBcA2AHAOgCxoDQgDMIEBnZAbVADsBDMRJEDACwBcxYR8BjAeytbwByECAC+uanQZMAVmR79BwxgHpVAAgDiEAG5CNNDX1gJurCPw0ByAMLWAOlT5USrDbY0BeDeQDsuACsuADMALoA3E5OLm7G-Nw07j4AFDS4GgBGAJTeAHyGGC6JrCk5UVQI7nxgAA68JPDeGikYbQSuuV4FKRBUEBY0sABqQ10FHSQYAE7wUACu3PAAShAA5mwpKbpDGR3jGh3bYxl9AxBDo7DZFVUatRC1TaltGJMHvf2DI2P5h64zOaLeBbHawPZUA5HMHZU5fC4-a4VGL8OJgGi1ZZApbNFIEebODQZGhrNazNZJXjTA4tGjcbgZbjzaazAQ0klk+AU1hUtL0vYE7gpJksoSsbLZFGudxEWCCaZYhY43G1WZQCAleDE0nkynUv58hnGZms8V-VVzDVJEEi025ABk9sMOq5esNuFtYtyAB9vYZ6cjnKj3DQYIFmgAPP5RgDUGkCFVi7ggJAAovoqM0NFHutmNABSDQAJm8PgAjIngxowPNYH5I9GNAAqDR+QN3AAquNY0xorgIVPRFn4BzVwOmfzOyQKPb7JAH0yHlioKTHS2mcIGNw0GmiQelGig8D4vcEAAU1VbBFmLerNY3c7eryCIw6nVHfb4wruqFK4qGoBLVJX0bOMi0DJNDFqWoanqRooGaB4nhaJwdzQncUhA3MoxbIsMlQ9CMKwgpYw0ABOXACMIzCDhw4sNAAWnjSjM0IlpiLzJiQgABh-G490g2d+0HJJl2aWxASVEEOxSdFMWxeBsjSaDYIaOYlOKJJYS-fjf33EheAQDBYF4NYUiE+cROHSEKh3VC9wcvSnC4EBGjMayyEYIsyyQbzxEkEBaHoEQihIBQQBcZRWBEcQwjEIA):

```js
// Given a collection 'C'
const C = [7,5,3];

const concat = (a, b) => a.concat(b);
let compose = (...fns) => (initialVal) => fns.reduceRight((val, fn) => fn(val), initialVal);
let pipe = (...fns) => (initialVal) => fns.reduce((val, fn) => fn(val), initialVal);

const mapReduce = (func , aggregator) =>  (acc, current) =>  aggregator(acc, func(current))
const filterReduce  = (predicate, aggregator) => (acc, current) => predicate(current) && aggregator(acc,current) || acc;

const add5 = x => x + 5;
const isEven =  x => x % 2 == 1;
const mul7 = x => x * 7;

let T = (transformation) => reducer => init => transformation(reducer, init);  

const decoratePredicate =  predicate => x => predicate(x) && x || [] 

const add2 = (x) => x + 2;

const app_piped = pipe (
       (x) => x * 2, 
       (x) => x + 9,
       (x) => x * 2 - 5,
       (x) => x - 30 
);

const transformation = C.reduce(T(mapReduce)(app_piped)(concat), []);

console.log(transformation);
```





At this point I still don't know with certainty what to call a transducer in my code.

But, I am happy anyway, I have reached an important part of my goal, which was to transform a collection while performing a single iteration on it.

I hope this article have gave you the curiosity to learn deeper 
on reducers an transducers.

Of course, in real life I would use a well established library like RamdaJS.

Don't  hesitate to comment and correct me.