-->

lodash curry does not work on function returned by

2019-06-13 16:31发布

问题:

Is the lodash flow function a real compose function, or is it something that looks like one, but is optimized to run fast and sacrifices the flexibility I'd expect? I expected flow to return a function I could curry, but instead it gave back a function that uses Javascript's arguments keyword. So curry can't tell that there are pending arguments, and it just gets invoked immediately.

Working intuitively enough:

var add = function(x, y) { 
    return x + y 
};
var exclam = function(x) { 
    return x.toString() + "!"; 
}
exclam(1) // "1!"
add(1,2) // 3
var add1 = FP.curry(add)(1);
add1(4) // 5
var add1AndExclam = FP.flow([add1, exclam])
add1AndExclam(2) // "3!"

Non-intuitive result:

addAndExclam = FP.flow([add, exclam])
/*
function(){
    var t=arguments,e=t[0];
    if(i&&1==t.length&&yi(e)&&e.length>=200)return i.plant(e).value();
    for(var u=0,t=r?n[u].apply(this,t):e;++u<r;)t=n[u].call(this,t);
    return t
}
*/
addAndExclam(1,2) // "3!"
add1AndExclamV2 = FP.curry(addAndExclam)(1) // "NaN!"`

Is it overkill to look for another library to help with functional programming paradigms? Should I just whip up my own compose? I used lodash because it was already in my project. The documentation makes it look like flow should be lodash's compose.

I've also found it really difficult to curry the data argument in lodash's each (I wanted something like an eachMyArrayName shortcut). Whether I use curryRight or the lodash object placeholder convention.

Is lodash FP just for making lodash functions auto curriable? Or am I doing something wrong, and it is usable as the main functional programming helper?

Edit:

If I want to I can wrap the function like this, but it seems to defeat the purpose of meta programming to have boilerplate looking code.

add1AndExclamV2 = FP.curry(function(x, y) { 
    return addAndExclam(x, y) 
})(1)
add1AndExclamV2(2)

"3!"`

回答1:

This is just basic function composition. Lodash "flow" and Rambda "pipe" probably found it difficult to name these functions because they're not suitably generic. They're not "real" the same way you use the word real in the phrase "real compose function".

You can compose a binary function with a unary function using comp2 – the catch is, the "binary" function must be in curried form instead of taking a tuple

let f = x => y => ... 

instead of

let f = (x,y) => ...

Recall that functional programming has its roots in the lambda calculus where there is no such thing as a function that takes anything other than 1 argument.

const curry = f => x => y => f (x,y)
const comp = f => g => x => f (g (x))
const comp2 = comp (comp) (comp)

var add = function(x, y) { return x + y };
var exclam = function(x) { return x.toString() + "!"; }

console.log(exclam (1)) // "1!"
console.log(add (1,2)) // 3

var add1 = curry (add) (1)
console.log(add1 (4)) // 5

var addAndExclam = comp2 (exclam) (curry (add))
console.log(addAndExclam (1) (2)) // "3!"

I encourage you to use the substitution model to see how the expression evaluates


Putting types on everything helps you reason about the program more effecitvely

// curry :: ((a,b) -> c) -> a -> b -> c
const curry = f => x => y => f (x,y)

// comp :: (b -> c) -> (a -> b) -> (a -> c)
const comp = f => g => x => f (g (x))

// comp2 :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
const comp2 = comp (comp) (comp)

// add :: (Number,Number) -> Number
var add = function(x, y) { return x + y };

// exclam :: Number -> String
var exclam = function(x) { return x.toString() + "!"; }

console.log(exclam (1)) // "1!"
console.log(add (1,2)) // 3

// add1 :: Number -> Number
var add1 = curry (add) (1)
console.log(add1 (4)) // 5

// addAndExlam :: Number -> Number -> String
var addAndExclam = comp2 (exclam) (curry (add))
console.log(addAndExclam (1) (2)) // "3!"

Regarding your comment:

I think if I wanted to compose a special function where one of the nested functions took two args, I would write it out

Good idea. If you find yourself looking around for a built-in procedure (provided by your language or some library), that should already be an indicator to you that you should try to write it out first. At least confirm with yourself that you're understanding your needs correctly.

This is perfectly acceptable

const addAndExclam = (x,y) => exclam (add (x,y))

And so is this

const addAndExclam = x => y => exclam (add (x,y))

If you later learn about comp2 and see that it could describe the code a little better, then you can implement it at that time

const addAndExclam = comp2 (exclam) (curry (add))


回答2:

var myAdd = function(x, y) { return x + y; }; var exclam = function(x) { return x.toString() + '!'; }; var addSclam = pipe(myAdd, exclam); addSclam(1,2); // "3!" var add1Sclam = curry( addSclam )(1); add1Sclam(500); // "501!"

It looks like ramda does what would have been intuitive to me. It adds another 1.2mb to my project, but I guess it could be used to more or less replace lodash.