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)
Related
I came across the following code for a callback function. I understand the code, but I can't wrap my head around it. It just seems so counterintuitive.
function greet(name,callback1){
callback1(name)
}
greet("John Cena", function (somerandomName) {
console.log(somerandomName);
return someRandomName;
}); // Output is John Cena and undefined.
This is what I have understood from the code:
We define a function greet, which takes 2 parameters name and callback1. Then we say that callback1's parameter is name. We don't return anything in the greet function, why?
And when we invoke the greet function we pass the 2nd parameter as an Anonymous function whose parameter is someRandomName. Then we console.log(someRandomName).
I added the return someRandomName, but this return did not work and I got the printed statement followed by undefined
Can someone please explain this in simple words, This just seems so counterintuitive.
So I think it is important to understand that functions themselves can be parameters.
In this example you pass a string as the first parameter and then a function that takes that string as a parameter as the second parameter.
Functions do not always need to return something. Often a function might perform a manipulation on the dom, fetch data, configure something or alter pre existing variables. Of course you can return something if that is what is needed.
Adding return like you did does not do much. In order to actually return the name value you would have to write the original function like this.
function greet(name,callback1){
return callback1(name)
}
Then you could do something like this
var wrestler = greet("John Cena", function (somerandomName) {
console.log(somerandomName);
return somerandomName;
});
console.log(wrestler) // prints John Cena
Its sort of an weird example since it serves no real purpose. Something like this might help you see whats going on.
function greet(name, callback) {
callback(name)
}
greet('John Cena', function(name){
console.log('Hello ' + name) // prints Hello John Cena
})
OR return something and use it to manipulate dom
function greet(name, callback) {
return callback(name)
}
var greeting = greet('John Cena', function(name){
return 'Hello ' + name
})
document.getElementById('message').innerHTML = greeting
// somewhere in HTML...
<h1 id='message'></h1>
Either way at least we are doing something to the first parameter now. And the things that you can do with callbacks are limitless.
Callbacks are a fundamental feature of javascript. They come into play alot when the first part of the function is asynchronous, such as a call to an api or database. In this case the first part would be the call to the database and the callback will not be fired until a value is obtained from the initial call. Recently, this callback pattern is being used less due to Promises, but callbacks are still useful.
So an example of a generic api call from a frontend to a backend. This would typically be done using the Fetch Api or with a library like Request or Axios. Just remember, the first function that is calling an endpoint takes some amount of time to execute and get data. The callback will not fire until that data is returned. Of course there would be a function on the backend that sends either an error or data back to the callback. I dont want to over complicate things, rather just give an idea of what callbacks are often used for.
function getDataFromBackend(endPoint, callback) {
callback(error, data)
}
getDataFromBackend('/api/user', function(error, data) {
if (error) {
// handle error - show user error message in ui, etc
}
// do something with data - such as display welcome message to user, etc
})
I would suggest working with callbacks for practice. I find that when I use Node, or build an app with a frontend and backend I implement callbacks a lot for, since there is a lot of asynchronous communication happening. Hope I answered your questions.
First, you have return someRandomName but your parameter is called somerandomName. Variables are case sensitive; that's why your return value is different from what you want.
Your question is Why don't we return anything in the greet function. The answer is "I have no idea." You could return something. Some functions return things; some functions don't. That has nothing to do with the callback arrangement here.
function greet(name,callback1){
return callback1(name)
}
var finalResult = greet("John Cena", function (someRandomName) {
console.log(someRandomName);
return someRandomName;
});
Now finalResult will be "John Cena".
If it helps at all, anywhere you're using an anonymous function, you could just as easily use a named function. (It's often uglier in practice, but for the sake of understanding the concepts...)
function greet(name,callback1){
return callback1(name)
}
function myGreeterFunction(someRandomName) {
console.log(someRandomName);
return someRandomName;
});
var finalResult = greet("John Cena", myGreeterFunction);
Now it's perhaps easier to see that callback1(name) is the same thing as saying myGreeterFunction(name) in this case.
This is a pretty dated approach to JavaScript and you are totally right that it's counter intuitive. In this particular example there's no advantage of writing this with a callback and probably should be written as a promise,
greet("John Cena")
.then(() => {
return 'Next Action'
})
.then(nextAction => {
console.log(nextAction)
})
And as #mark-meyer pointed out this approach would be required if you had an asynchronous event.
Actually an AWS Lamda function actually has an optional callback defined as it's third argument. exports.myHandler = function(event, context, callback) {}. Which to be brutally honest I think is only there to cover cases where third party libraries aren't promised based.
Whilst passing a callback to a function is probably never the correct approach, there's still cases where passing a function to a function is valuable. Perhaps in a pub/sub system.
const events = {}
function subscribe (event, func) {
if (events[event]) {
events[event].push(func)
} else {
events[event] = [func]
}
}
function publish (event) {
if (events[events]) {
events[event].forEach(func => func())
}
}
And if you are writing with a fp approach link in RamdaJs chaining functions is going to be a huge part of what you write.
Does that clear things up a bit #pi2018?
The general technique is called continuation-passing style – contrast with direct style.
The technique does allow for some interesting programs to emerge. Below, we have two ordinary functions add and mult. We write two programs using cont which allows us to string these functions together, where each step is
the continuation of the one before it.
const cont = x => k =>
cont (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
cont (1) (add (2)) (mult (3)) (console.log) // 9
// 1 ->
// x => 2 + x
// cont (3) ->
// x => 3 * x
// cont (9) ->
// x => console.log (x)
// cont (undefined)
We can sequence as many operations as needed –
const cont = x => k =>
cont (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
cont (2) (mult (2)) (mult (2)) (mult (2)) (mult (2)) (console.log) // 32
// 2 ->
// x => 2 * x
// cont (4) ->
// x => 2 * x
// cont (8) ->
// x => 2 * x
// cont (16) ->
// x => 2 * x
// cont (32) ->
// x => console.log (x)
// cont (undefined)
Continuation-passing style can be used to implement concurrent programs, but JavaScript provides a better native concurrency primitive now, Promise. There's even new async and await syntax to make working with Promises easier.
As wikipedia notes, continuation-passing style is used more by compilers and less by programmers. If you're trying to write concurrent programs, I would highly recommend you use Promises instead. Later versions of Node include util.promisify which allows users to convert a continuation-passing style function to a Promise-returning async function.
I have a series of (pure) functions responsible for expanding tokens held in an object graph.
I won't go into the details as they aren't important, but they are something like:
expandData => expandDataNodes => expandDataNode => expandDataNodeItem
I have everything working fine, but I now have a new requirement which means I now need user defined configuration to be available in expandDataNodeItem. Obviously in the OO world this is a trivial problem to solve just make the config available on the DataExpander class via a getter and pull it out when needed, but I'm interested in the options for handling this situation when using a functional paradigm.
I know I can add a param to each of these functions and pass down either the option or a function for accessing the option, but this results in lots of functions which have no interest in the option getting it added to their signatures which feels like it really muddies things.
The best option I can think of at the moment is to wrap all these functions in another function and use closure to make the options available to all functions within, but is there a better way?
With these few details I would try another approach: you may refactor your code so the functionality that requires some configuration may come already injected in a partially-applied function. That is, your current function should be a high-order function.
Check the following sample on which I demonstrate that approach:
const f = ( { config1, configN } ) => x => config1 + x
const g = f => x => f ( x )
const settings = {
config1: 1
}
// _f would be the function that you would provide
// when you need to do certain operation based on
// who knows what configuration
const _f = f ( settings )
const y = g ( _f ) ( 2 )
console.log( y )
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
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.
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