简体   繁体   中英

How to pass multiple arguments to compose with Ramda.JS

I'm using Ramda.js and I am struggling to accomplish a simple task. Could someone please tell me: 1) Why it doesn't work and 2) How can I make it work

Consider the following code:

// Defined in utils.js
const makeArrayFromNum = num => [num]
const multByTen = num => num * 10
const multByTwo = num => num * 2
const makeThing = compose(
  map(multByTwo),
  concat,
  map(multByTen),
  makeArrayFromNum
)

// Otherfile.js
// import makeThing from .......
// Call it from a different file (does not share scope with above things)
// ---------------
// Expect to get back: [100, 18] because:
// 1) 5 -> makeArrayFromNum -> [5]
// 2) [5] -> map(multByTen) -> [50]
// 3) [50] -> concat (Still needs one arg, so pulls in [9]) -> [50, 9]
// 4) [50, 9] -> map(multByTwo) -> [100, 18]
makeThing(5, [9])

When I invoke makeThing , I first pass 5 to it. makeArrayFromNum accepts the 5 and everything goes smoothly... that is, until concat . concat returns a function. Because concat takes 2 arguments, I would expect it to go back to my original argument list, and locate the [9] that has not been used yet. It does not.

This should be a link to the Ramda REPL, with the same code I pasted above. But at least you can run it there.

Thanks

UPDATE

The above code snippet doesn't work, but I wrote it because it demonstrates in a clear way what I'm trying to achieve. Since it attracted the wrong kind of attention, I've included two more functioning code snippets, which also demonstrate my goal, but in an unclear and non-concise way.

Since someone in the comments said it wasn't possible to do with compose since compose only accepts unary functions, I felt I should provide a solution that I came up with to help clarify my question. Hope this helps others answer my question.

const makeThing = compose(
  map(multByTwo),
  useWith(
    concat,
    [
      compose(
        map(multByTen),
        makeArrayFromNum
      ),
     identity
    ]
  )
)
makeThing(5, [9])

And another for posterity:

const makeThing = compose(
  map(multByTwo),
  converge(
    concat,
    [
      compose(
        map(multByTen),
        makeArrayFromNum,
        head
      ),
      tail
    ]
  )
)

My original question still stands. What the above solutions gain in functionality, they lack in clarity and conciseness.

First the following functions could be transformed as follows:

  1. num => [num] is equal to R.of
  2. num => num * 10 is equal to R.multiply(10)
  3. num => num * 2 is equal to R.multiply(2)

I can see two options:

Have a function that returns another function

As you can see the function composition is "hardcoded" to the num parameter. Note that I also slightly modified your original function composition

const makeThingFn = num =>
  compose(
    map(multiply(2)),
    flip(prepend)([num]),
    multiply(10));

const makeThing = makeThingFn(9);
makeThing(5); //=> [100, 18]

Use lenses

That way you can pass your array directly but have the function composition operate on specific indexes:

const makeThing =
  compose(
    map(multiply(2)),
    over(lensIndex(0), multiply(10)));

makeThing([5, 9]); //=> [100, 18]

It doesn't work because compose is, quite literally, just composing the functions . That means all of the parameters are passed to the last/rightmost function, then the return value of that function is passed to the next function on the list, and so on. Thus, your makeThing function is essentially this:

const makeThing = (...params) => map(multByTwo)(concat(map(multByTen)(makeArrayFromNum(...params))))

This means that makeThing(5, [9]) translates to the following call:

map(multByTwo)(concat(map(multByTen)(makeArrayFromNum(5, [9]))))

Note that concat is being called with only one parameter, but it requires two. Since Ramda functions are automatically curried, the incomplete concat returns another function that is expecting the second parameter. Since no second parameter is being passed, things fall apart at that point.

So how can you do it? What you need is something like this:

const makeThing = (a, b) => map(multByTwo)(concat(map(multByTen)(makeArrayFromNum(a)), b))

// alternatively...
const makeThing = (a, b) => map(multByTwo)(concat(map(multByTen)(makeArrayFromNum(a)))(b))

Try it out and you'll see that it gives you the expected output. Unfortunately, that doesn't directly translate back to compose ; the closest you can get is this:

const makeThing = (a, b) => compose(
  map(multByTwo),
  concat(b),
  map(multByTen),
  makeArrayFromNum
)(a)

You get a result that is close, but the items are reversed. Maybe there are some Ramda functions you can use to handle that, but I don't know all the functions so I can't help there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM