I am trying to better understand functional composition in ramda.js and currently have a method in a class that looks like this:
const replace = (newItem) => function3(function1(newItem).function2(newItem));
I know in ramda.js for a simpler function like
const replace = (newItem) => function2(function1(newItem));
you could write it like
const replace = compose(function2, function1);
Is there a similar way to do the same with the initial function using functional composition / application or other ramda.js helper methods?
Ramda has two functions that should help with this. The more standard one is lift. Many functional languages have this concept. One way to think about it is that it lifts a function which operates on values to create one that operates on containers of those values:
add(3, 5) //=> 8
lift(add)([3], [5]) //=> [8]
Functions can be seen as containers of values too. Functions which return values of a given type can be considered containers for that type.
So we can lift your function3 to operate not on values, but on containers for those values, and then supply it the input to those functions. Here's an example with arrays as containers:
const function1 = newItem => `function1(${newItem})`
const function2 = newItem => `function2(${newItem})`
const function3 = (v1, v2) => `function3(${v1}, ${v2})`
const combine = R.lift(function3)(function1, function2)
console.log(combine('foo')) //=> "function3(function1(foo), function2(foo))"
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
The less standard function is converge. This is focused only on functions, and not on arbitrary containers. It works similarly in this case. The function is created in one pass rather than the two for lift. And that means the initial functions need to be wrapped in an array:
const function1 = newItem => `function1(${newItem})`
const function2 = newItem => `function2(${newItem})`
const function3 = (v1, v2) => `function3(${v1}, ${v2})`
const combine = R.converge(function3, [function1, function2])
console.log(combine('bar')) //=> "function3(function1(bar), function2(bar))"
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
converge is only for functions, but it can work with polyadic functions. lift will work only with unary ones. converge is a Ramda-specific function. I haven't seen it elsewhere. So, if lift will work for you, I would suggest you choose it instead.
So your question is how to write
function1(input).function2(input)
In a functional way. If I am correct, here is how:
First let's create a function method that would give us a method of an object bound to that object:
const method = R.curry((name, object) => R.bind(object[name], object))
Using this function, we can rewrite our expression as
method('function2', function1(input))(input)
But we want something cleaner and more re-usable, right? So let's do some refactoring
method('function2', function1(input))(input)
method('function2')(function1(input))(input)
R.pipe(function1, method('function2'))(input)(input) // or use R.compose
R.converge(R.call, [
R.pipe(function1, method('function2')),
R.identity
])(input)
Now we can define our function combine like so
const combine = (fn, name) =>
R.converge(R.call, [
R.pipe(fn, method(name)),
R.identity
])
And the expression becomes
combine(function1, 'function2')(input)
I hope my explanation is clear and it solves your problem :)
Related
I'm trying to write a function composition that partially applies an argument at each step and ends up calling a curried two-argument function.
There is a set of example functions to compose. I removed the calculations that there are supposed to do as they are not relevant to the problem but let's assume that every argument is required.
const getDayLimit = () => {
return 10
}
const getIpCount = ip => dayLimit => {
return 99
}
const getIp = (deviceId, headerIp) => {
// TODO: use Result monad to handle errors
if (!deviceId && !headerIp) {
throw new Error('Ip not provided')
}
return deviceId || headerIp
}
And the composition attempt:
const validateIp = R.compose(
f => f(getDayLimit()),
getIpCount,
getIp
)
validateIp(1, 2)
In the first step, getIp received two values and based on them returns an ip that is then partially applied to getIpCount, now the composition return a function that expects the dayLimit argument that needs to be computed first.
The plain way of doing this could be: f => f(getAccountLimit()).
I'd like to remove such function creation f => f... and pass it point-free.
Here's a helper function that solves this but is not handling all cases such as passing arguments to the result function:
const applyResult = result => f => R.compose(f, result)()
then I could do:
const result = R.compose(
applyResult(getDayLimit),
getIpCount,
getIp
)
It seems too hacky for me and not substantial for my further use. I'd rather avoid writing my own helper function for this kind of problem.
Is there a functional way of computing arguments before partially applying them to a function? It seems to be a pretty common case in my mind, though perhaps I'm not thinking about the problem correctly.
Is my thinking incorrect about this problem and function composition?
What is a good approach to handling such a case with a function with two parameters in a composition?
Can this case of partially applying function arguments with each step be handled in a function composition?
Thank you!
I think I would use a continuation which, as I understand it, represents a computation that has been interrupted:
const cont = x => f => f(x);
With a continuation, you get x before f. Instead of doing f(x) you do cont(x)(f) which behind the scene just does f(x) for you.
At the time of composing the functions together you already know the value of x which is getDayLimit(), you just don't know the value of f yet which is known only when result is applied to the first two initial parameters.
So here's what I'd do:
const result = R.compose( cont(getDayLimit())
, getIpCount
, getIp);
Is there a functional way of computing arguments before partially applying them to a function?
I would simply note that you apply a function to a value (not the other way round)
I'm learning functional programming and I'm trying to refactor a piece of old code using lodash FP.
Here's the code I have:
_.filter(x => isIdInArray(x)(xs))(otherXs)
It's too complicated to read and makes me feel a bit weird (smell ?)
My problem is that x value, that is the first argument of isIdInArray is declared this way:
const getId = _.get('id');
const isIdInArray = _.compose(_.includes, getId);
I can't use my lodash filter function this way:
_.filter(isIdInArray(xs))(otherXs)
I don't even know if it's feasible, but I'm pretty sure I can do something clearer or more readable.
Do you have some ideas ?
Try not to stuff all the fancy features that lodash gives you into a single line there.
Having a complex mechanism in one line may seem nice, but if you can't read it anymore its not very helpful at all.
For managing collections i usually use approaches like this:
var collection = [{id: 'a', someField: 1}, {id:'b', someField: 2}];
var theIdsYoureLookingFor = ['a'];
var filtered = collection
.filter(anyObject => _.includes(theIdsYoureLookingFor, anyObject.id))
.map(anyObject => anyObject.someField);
alert(filtered); // alerts 1
Which parses a collection of objects, filters for those who have an id that you consider valid and then maps those objects to a certain field.
Also don't ever use variable names like: x, xs
If you're writing production code, I recommend using a higher-level function. In your particular case I'd say you need _.intersectionBy:
const keepIfIdInArray = _.intersectionBy('id'); // 'id' can be replaced by your getId
const keepIfIdInOtherXs = keepIfIdInArray(otherXs);
keepIfIdInOtherXs(xs);
If you're doing this as an exercice, then I'd say you may need to decompose a little more. Notice that in lodash/fp, _.includes is curried so you should be able to write the following:
const getId = _.get('id');
const isIdInArray = arr => _.compose(_.includes(arr), getId);
const isIdInOtherXs = isIdInArray(otherXs);
_.filter(isIdInOtherXs)(xs);
I am composing a series of function but I wonder what's the best way to achieve what I want first this is how I compose:
const composeP = (...fns) => fns.reduce((f, g) => async (...args) => f(await g(...args)))
const profileSummary = profileData => composeP(createProfileSummary, getMKAProfile)(profileData)
now what I want is to do a check and if profileData which is my input is a certain string e.g. "cantbesearched" I want to return a value immediately to "profileSummary" variable instead of executing previous functions...
so is it possible to create a "filterWords" function, put it in front of the composition like this:
const profileSummary = profileData => composeP(createProfileSummary, getMKAProfile, filterWords)(profileData)
and if certain words are detected, skip previous functions on the left then return a value.
Is it possible to create a "filterWords" function to be put it in front of the composition?
No. What you want to do is branching, which is not possible with function composition.
What you can do is compose functions that work on a type which provides an error path, like Maybe or Either. (You can also consider exceptions as a builtin error path for every type, so just throw).
Oh wait, you already are doing that! You didn't write a plain function composition compose, you wrote composeP which uses monadic Kleisli composition - and promises do have such an error path:
function filterWords(word) {
return word == "cantbesearched"
? Promise.reject(new Error("filtered for your safety"))
: Promise.resolve(word);
}
Background:
Composition is putting two functions together to form a third function where the output of one function is the input of the other.
No matter how much I look at this I struggle with how to read it. In particular why the compose() return => (a) => captures the 121.2121212 in local scope. Also I struggle with how final fn f(g(a)) would look with all the values/fn present w/o the use of variables.
Question: Does anyone have any techniques or diagrams for quickly reading examples like this; how can I mentally debug and follow the function flow?
Reference:
const compose = (f, g) => (a) => f(g(a)) // Definition
const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage
floorAndToString(121.212121) // '121'
As mentioned by T.J. Crowder, it often helps rewriting arrow functions as regular functions. So the function:
const compose = (f, g) => (a) => f(g(a))
Can be rewritten as:
function compose (f, g) {
return function (a) {
return f(g(a));
}
}
Now it is perhaps more obvious what's going on. So now let's rewrite the other parts:
const floorAndToString = compose((val) => val.toString(), Math.floor)
Can be rewritten as:
function convertToString (val) { return val.toString() };
const floorAndToString = compose(convertToString, Math.floor);
Now it may be more obvious that the compose function will return the function:
// remember that we pass `convertToString` as `f`
// and `Math.floor` as `g`:
function (a) {
return convertToString(Math.floor(a));
}
So it's obvious that the function floorAndToString simply returns the result of convertToString(Math.floor(a)). There is nothing special about compose that captures 121.2121212 because it doesn't. Instead it creates a function where 121.2121212 can be passed as an argument to convertToString(Math.floor(a)).
It might help to look at the Wikipedia article for function composition. But I think your problem is not really related to function composition but to the arrow notation in general.
Maybe it helps to look at a simpler example first:
const addOne = (x) => x + 1
const addN = (n) => (x) => x + n
const addSeven = addN(7)
The last line produces a new function that adds seven to the input (x) => x + 7. You can think of the parameter tuples between the arrows as being filled from left to right when values are provided (and the variables to the right are bound to these values). As long as you don't provide all parameters, you will obtain a new function.
You can also provide all parameters like this:
addN(5)(3) // yields 8
Note that addN can be seen as taking two parameters but in separate bracket pairs. The arrows between the brackets in the definition kind of allow you to omit parameters to the right and obtain a function with fewer parameters with the left ones being already fixed.
Let's look at an alternative definition of compose:
const weirdCompose = (f, g, a) => f(g(a))
It should be clear how it works, but the problem is that you cannot use this to compose two functions without evaluating the result of the composition with the value a right away. By separating the parameters into two groups you can partially apply the function and only provide f and g in a first step.
To understand this better, I suggest you also have a look at the concept of currying
I decided to code a simple todo app using Ramda, but I have been stuck with one refactoring related issue. Here're two functions that I think could be refactored:
const isItemCompleted = R.pipe(
R.prop("states"),
R.contains("completed")
)
const isItemEdited = R.pipe(
R.prop("states"),
R.contains("editing")
);
As you can see, there is some code duplication and this would get even messier if I had more states. I have been trying to isolate the duplicated functionality as such:
const statesContains = R.flip(R.pipe(
R.prop('states'),
R.contains()
))
//I would like to use it like this:
const isItemCompleted = statesContains("completed")
const isItemEdited = statesContains("editing")
But I just cannot wrap my head around this. I can make it work with different argument ordering, but I would like to follow the data-last rule to create concise functions.
The data being passed to these isItemCompleted and isItemEdited functions could be something like this:
let item = {states:["editing", "complete"]};
isItemCompleted(item); //true
Any (functional) ideas?
There are several ways to go with this.
Perhaps the most straightforward is
const statesContains = R.curry(
(state, item) => R.contains(state, R.prop('states', item))
);
const isItemCompleted = statesContains("completed");
But it's reasonable to want to abstract this a bit, to allow the property to be searched to vary as well. So you could write:
const propContains = R.curry(
(propName, state, item) => R.contains(state, R.prop(propName, item))
);
const editorsContains = propContains('editors')
const edFred = editorsContains('fred');
// or edFred = propContains('editors', 'fred');
Both of these are reasonable. But Ramda has a function which reads really well, and will serve these needs pretty well, where. With this, you can simply write:
const isItemCompleted = R.where({states: R.contains('completed')});
This, I believe, is the simplest approach if you're looking for one-offs. But both of the above could help you create reusable functions.
You can see all this in action in the Ramda REPL