I'm learning to use Ramda and have e across pose. But I can't grasp the purpose of it
let value = Rpose( calledThird, calledSecond, calledFirst('hello') );
// vs
let value = calledThird( calledSecond( calledFirst('hello') ) );
Is it purely to allow currying? Perhaps...
let curried = Rpose( calledThird, calledSecond, calledFirst );
curried('hello');
Is there any other purpose?
I'm learning to use Ramda and have e across pose. But I can't grasp the purpose of it
let value = R.pose( calledThird, calledSecond, calledFirst('hello') );
// vs
let value = calledThird( calledSecond( calledFirst('hello') ) );
Is it purely to allow currying? Perhaps...
let curried = R.pose( calledThird, calledSecond, calledFirst );
curried('hello');
Is there any other purpose?
Share Improve this question edited Jan 19, 2016 at 13:32 thomas-peter asked Jan 19, 2016 at 13:26 thomas-peterthomas-peter 7,9546 gold badges47 silver badges59 bronze badges 1-
2
Iv'e only got a theory understanding of functional programming never used it in practice, yet. However pose generates a new method which than can be executed at a later date, or used in a new position. Your two 'vs' are not doing the same thing. The second one is executing each function individually passing the value into each. Granted that's what pose does under the skin, whilst the first returns a new function for you to use where you like. Value one will be a function, value two will be the response from
calledThird
. Value one is the posed function to execute at a later date. – ste2425 Commented Jan 19, 2016 at 13:37
2 Answers
Reset to default 13Your second example is exactly the reason for it, although this in fact has nothing to do with currying.
Functional position allows you to build more sophisticated functions out of simpler ones.
Imagine you have some datatype that you need to sort, let's say a collection of appointments. Given that you already have a sortByDate
function and a reverse
function, you can write sortByDateDescending
as
var sortByDateDescending = function(appointments) {
return reverse(sortByDate(appointments));
}
or in ES6:
const sortByDateDescending = appointments => reverse(sortByDate(appointments));
There is nothing wrong with this. But if you were to write it with a pose
helper, it has several advantages:
var sortByDateDescending = pose(reverse, sortByDate);
First of all, it's clearly shorter, and the difference will grow more substantial as you add more functions, especially with pre-es6 code.
But more importantly, this allows you to focus on what's meaningful. You are bining functions here; the data that will eventually be passed through is the goal, but while you're building the new function, it's mostly a distraction.
By writing it this way, you get to focus on what's going on: you are sorting the list by date, and then you are reversing the result.
Languages more closely focused on functional programming make this simpler still, with an unobtrusive operator rather than a function like pose
. In Haskell, the equivalent would look like
sortByDateDescending = reverse . sortByDate
But Javascript does not offer that elegance. The best we can do is create functions like pose
(or its order-reversed twin, pipe
.)
My introduction to Ramda post offers many more examples of working in this style.
Currying, by the way, is a different beast altogether. It is one technique that makes it much easier to reuse functions in such positions. But it's mostly a distraction here. If you're interested, I also have a post on the issue.
Scott's answer is great - just wanted to add some more real-life examples.
You can identify places to improve code when you see a lot of this sort of pattern, where you're constantly massaging some data with passthrough functions:
var list = [3, 4, 1, 2];
list = filterOutEvens(list);
list = sort(list);
list = prependFoo(list);
You may be tempted to do something like this instead:
[3, 4, 1, 2].filterOutEvens().sort().prependFoo();
But then you remember that to make that possible, you'd have to add stuff to Array.prototype, which is definitely a no-no.
R.pose gives you the next best thing (remember that it works right-to-left):
var processList = R.pose(prependFoo, sort, filterOutEvens);
processList([3, 4, 1, 2]);
Here's the full example if you want to play around with it:
function filterOutEvens(list) {
return list.filter(function(item) {
return item % 2 !== 0;
});
}
function sort(list) {
// More cleanly use R.sort here instead of native Array.prototype.sort, which operates in-place.
var diff = function(a, b) { return a - b; };
return R.sort(diff, list);
}
function prependFoo(list) {
return list.map(function(item) {
return 'foo-' + item;
});
}
var processList = R.pose(prependFoo, sort, filterOutEvens);
var result = processList([3, 4, 1, 2]);
// -> ["foo-1", "foo-3"]
// Display Stackoverflow snippet output.
document.body.innerHTML = JSON.stringify(result, null, 2);
<script src="//cdn.jsdelivr/ramda/0.19.1/ramda.min.js"></script>