Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compose functions and then apply arguments in Lodash/FP

I am trying to learn more about using currying and composition in functional programming by using Lodash/FP to clean up some old code. However, I am repeatedly running into situations where I have a function and I want to pass it one or more functions. I then want to pass the values that will be used as the arguments to the functions that I passed to the original function.

I'm finding it difficult to explain exactly what I'm trying to do so I made a JS Fiddle that shows how I have been trying to approach this: https://jsfiddle.net/harimau777/rqkLf1rg/2/

const foo = a => `${a}${a}`
// Desired Behavior: const ans1 = (a, b) => `${foo(a)}${foo(b)}`
const ans1 = _.compose(
    (a, b) => `${a}${b}`,
  foo,
  foo
)
// Desired Result: '1122'
console.log(ans1('1', '2'))

// Desired Behavior: const ans2 = a => a.map(a => a + 1)
const ans2 = _.compose(
    _.map,
  a => a + 1
)
//Desired Result: [2, 3, 4]
console.log(ans2([1, 2, 3]))

Based on Ori Drori's answer below I think that I can clarify my question (is this how people normally follow up on StackOverflow as opposed to asking a new question?): Suppose that instead of applying the same sequence of functions to both inputs I wanted to apply a sequence of functions to the first input, a different sequence to the second input, and use both results as the input to the rest of the _.compose. I could do this using:

const f1 = _.compose(<Some sequence of functions>)
const f2 = _.compose(<Some sequence of functions>)
const f3 = <A function which takes two inputs>
const ans = _.compose(
  <More functions here>,
  f3
)(f1(a), f2(b))
console.log(ans)

However, I'm wondering if there is a way to handle this using a single compose or if there are any patterns that tend to be used in functional programming to handle situations like this.

like image 752
David Moneysmith Avatar asked Mar 07 '23 02:03

David Moneysmith


1 Answers

LodashFPs _.compose() (_.flowRight() in lodash) works by applying the parameters to the right most (bottom in your code) function, and passes the result to the function to it's left, and so on:

_.compose(a, b, c)(params) -> a(b(c(params)))

This means that every function, except for the right most receives only one parameter.

You can get the 1st example working by changing the methods a bit:

const ans1 = _.compose(
  arr => arr.join(''), // joins the params to one string
  (...args) => args.map((s) => `${s}${s}`) // returns an array of double params
)

// Desired Result: '1122'
console.log(ans1('1', '2'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.min.js"></script>

In the 2nd example you want to create a new method that maps via a predefined callback. Since lodash/fp methods are auto curried, you can supply the callback to the _.map(), and get a new method. Compose won't work here since _.map() doesn't use the results of the method directly, but applies it to every item in the array:

const ans2 = _.map(a => a + 1)

//Desired Result: [2, 3, 4]
console.log(ans2([1, 2, 3]))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.min.js"></script>

The case you've presented in your clarification can be handled by _.useWith() (known as _.overArgs() in lodash):

Creates a function that invokes func with its arguments transformed.

However, it's use is not recommended since it reduces readability.

Example:

const foo = a => `${a}${a}`

const ans1 = _.useWith(
  (a, b) => `${a}${b}`,
  [
    foo,
    foo
  ]
)

const result = ans1(1, 2)

console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
like image 152
Ori Drori Avatar answered Mar 27 '23 16:03

Ori Drori