Reputation: 5008
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.
Upvotes: 1
Views: 1413
Reputation: 18941
First the following functions could be transformed as follows:
num => [num]
is equal to R.of
num => num * 10
is equal to R.multiply(10)
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]
Upvotes: 5
Reputation: 5613
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.
Upvotes: 0