Bergi's answer is great. But another way to think about this is to get a little more specific. Ramda really needs to include a non-list example in its documentation, as lists don't really capture this.
Lets take a simple function:
var add3 = (a, b, c) => a + b + c;
This operates on three numbers. But what if you had containers holding numbers? Perhaps we have Maybe
s. We can't simply add them together:
const Just = Maybe.Just, Nothing = Maybe.Nothing;
add3(Just(10), Just(15), Just(17)); //=> ERROR!
(Ok, this is Javascript, it will not actually throw an error here, just try to concatenate thing it shouldn't... but it definitely doesn't do what you want!)
If we could lift that function up to the level of containers, it would make our life easier. What Bergi pointed out as lift3
is implemented in Ramda with liftN(3, fn)
, and a gloss, lift(fn)
that simply uses the arity of the function supplied. So, we can do:
const madd3 = R.lift(add3);
madd3(Just(10), Just(15), Just(17)); //=> Just(42)
madd3(Just(10), Nothing(), Just(17)); //=> Nothing()
But this lifted function doesn't know anything specific about our containers, only that they implement ap
. Ramda implements ap
for lists in a way similar to applying the function to the tuples in the crossproduct of the lists, so we can also do this:
madd3([100, 200], [30, 40], [5, 6, 7]);
//=> [135, 136, 137, 145, 146, 147, 235, 236, 237, 245, 246, 247]
That is how I think about lift
. It takes a function that works at the level of some values and lifts it up to a function that works at the level of containers of those values.