Function composition early return - javascript

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);
}

Related

Ramda point-free problems with transform functions (map, etc)

I have this function, which is basically mapping request parameters and query parameters to a SQL statement:
function selectSingleResult(params, { order, limit, offset, fields, runset_id, txOffset, txFactor }) {
const transformer = R.when(R.equals('values'), R.compose(knex.raw.bind(knex), applyConversion({ txOffset, txFactor })))
const newFields = R.map(transformer , fields);
knex('myTable').select(...newFields) // etc...
Ideally, I would like to be able to define transformer outside the function so the function can just become:
const mapFields = R.map(transformer);
function selectSingleResult(params, { order, limit, offset, fields, runset_id, txOffset, txFactor }) {
knex('myTable').select(...mapFields(fields)) // etc...
The issue being that the applyConversion function needs arguments given to selectSingleResult.
This is a common issue I have with transform functions to map. They often require parameters other than the values being mapped over. In such cases, how can I write in point-free, or at least more testable style and not end up nesting functions so much?
It feels as though you're trying to go point-free in code where it doesn't make much sense. But there's a contrasting notion of destructuring a large set of fields from the second parameter that doesn't seem necessary, so this seems mostly confused. (It might actually be necessary: perhaps you're using order, limit, offset, and runset_id in the remainder of your main function.)
I think you can accomplish almost what you're asking by just introducing one more layer of calls, with something like this:
const transformer = (query) =>
R .when (R .equals ('values'), field => knex .raw (applyConversion (query) (field)))
const mapFields = R.pipe (transformer, map)
const selectSingleResult = (params, query) => {
knex ('myTable') .select (... mapFields (query) (query .fields))
// etc...
}
I name the second parameter to your main function query; it's a guess, and if that's confusing, replace all the instances of query with foo or with something meaningful to you.
Note that mapFields could also be written as const mapFields = (query) => R.map (transformer (query)), but the version above seems simpler. Also note that I simplified transformer a bit. I simply don't see any reason to try to go point-free, when you can't get all the way there. And trying to mix point-free code with OO constructs such as knex .raw just seems to confuse things.
If I read the code correctly, we also might rewrite transformer like this:
const transformer = (query) => (field) =>
field == 'values' ? knex .raw (applyConversion (query) ('values')) : field
but I can't decide if that is an improvement or not.

Partially applying function arguments in a composition

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)

compose chained functions using the same parameter in javascript

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 :)

Why is this attempt at writing functional code with lodash-fp not behaving as I expect?

Background
It's my understanding that by currying a function, you instruct it to return a function if it's arity is not met and this mechanism is key to understanding functional composition. I've found many tutorials online, but not many that demonstrate this using lodash-fp and user-defined-functions. It appears that I'm missing a concept in the below code snippet, because my composed function throws an error when I invoke it with no arguments.
Problem
I expect the final function call below to return a function, waiting for the data to be passed in, but instead it attempts to flow through each function. These two functions can be called independently and they return functions instead of attempting to execute the logic with no arguments. What am I missing that causes a runtime error when trying to execute the combined function with no arguments?
Complete Code
const compose = require('lodash/fp/compose');
const curry = require('lodash/fp/curry');
const toUpper = curry(input => {
return input.toUpperCase()
})
//This returns a function, which is correct because it's a curried function and it's arity has not been satisfied
console.log(toUpper())
//This returns what I expect, which is "HELLO WORLD"
console.log(toUpper("hello world"))
const reverseString = curry(input => {
return input.split("").reverse().join("")
})
//"olleh" looks right to me!
console.log(reverseString("hello"))
//Let's try putting it all together with function composition
const uppercaseAndReverse = compose(reverseString, toUpper)
//"OLLEH" is exactly what I expected
console.log(uppercaseAndReverse("hello"))
//ERROR: input.split is not a function
console.log(uppercaseAndReverse())
The RunKit version of this code is here
This is because the function being curried, reverseString, is composed with another function, lodash#toUpper. Since an invocation of a composed function using lodash#compose is evaluated from last to first. A call from uppercaseAndReverse() invokes lodash#toUpper() first which returns undefined, and passes it to reverseString that treats it as a parameter.
If you wish for the uppercaseAndReverse composition to curry then use lodash#curryN instead wherein the first parameter is the arity and the second parameter is the function to curry:
const uppercaseAndReverse = curryN(1, compose(reverseString, toUpper))
const { compose, curry, curryN } = _;
const toUpper = curry(input => {
return input.toUpperCase()
})
//This returns a function, which is correct because it's a curried function and it's arity has not been satisfied
console.log(toUpper())
//This returns what I expect, which is "HELLO WORLD"
console.log(toUpper("hello world"))
const reverseString = curry(input => {
return input.split("").reverse().join("")
})
//"olleh" looks right to me!
console.log(reverseString("hello"))
//Let's try putting it all together with function composition
const uppercaseAndReverse = curryN(1, compose(reverseString, toUpper))
//"OLLEH" is exactly what I expected
console.log(uppercaseAndReverse("hello"))
//ERROR: input.split is not a function
console.log(uppercaseAndReverse())
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
Note that a composed function does not contain any arity, hence, simply using lodash#curry wouldn't work.

How do I read functional composition in es6/javascript?

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

Categories