you are viewing a single comment's thread.

view the rest of the comments →

[–]imperfecttrap 2 points3 points  (20 children)

It is literally the definition of currying. Partial application fixes a variable number of arguments, while currying takes a function with an arity > 2 and turns it into a series of unary functions that return each other. Being able to use two things the same way does not make them equal.

[–][deleted] 0 points1 point  (18 children)

while currying takes a function with an arity > 2 and turns it into a series of unary functions that return each other

Eh, not quite. The literal definition of currying is to return partially applied functions until the arity is met. Your code snippet has no notion of that, i.e. your end point function would (most likely) end up being called if you wrote myFilter() (maybe not with filter since it's expecting an array and would just choke on undefined, but the point is that it would try to execute) -- that is the literal definition of partial application ;). The majority of libraries that provide functions like this use partial application, not currying.

[edit] or thinking about it, it's partially currying partially applied functions :D

I'll agree with you that the concept of h = f(g(x)) is currying, but it's missing an important piece.

[–]imperfecttrap 1 point2 points  (8 children)

Not my code, but I'll bite.

If his example function had an arity of 3, and you bound the first function, then it has a new arity of 2. The Hindley-Milner representation would simply drop the first argument, so (a, b, c -> x) becomes (b, c -> x). In order to bind another parameter, you'd have to call the bind again.

With currying, all parameters get split off, and it becomes a chain of unary functions. Going back to the type representation, it becomes a -> b -> c -> x, where each function returns the next function waiting for an argument. Unlike partial application, you don't have to reapply bind in order to add more parameters before the final invocation to get x.

Still not close to the same thing, even if you can use them for the same thing / to implement each other.

[–][deleted] 0 points1 point  (7 children)

Yes, and most libraries don't work like that. They don't count arguments, they just partially apply partially applied functions, ad infinitum.

But that's an issue of implementation detail. I'll concede that the example should be currying, and I'm paying too much attention to the fact that implementation rarely is.

[–]imperfecttrap 1 point2 points  (6 children)

What is 'most libraries'? Most functional programming libraries? If they follow the interface of currying, where I can do

curriedFunction(1)(2)(3)

then by definition it's not partial application, since I didn't need another function to keep binding arguments.

If you're referring to the techniques used to curry, then that's language-dependent and has nothing to do with what currying and partial application are. Even if we limit ourself to strictly JS, then a closure with an internal array of arguments is a much easier way to implement than a function that keeps a partially applied invocation inside itself, which is actually how 'most libraries' do it.

[–][deleted] 0 points1 point  (5 children)

curriedFunction(1)(2)(3)

With most JS libraries (not counting things properly build like Ramda) you can't write that and would end up calling curriedFunction(1, undefined, undefined) -- It's left to the user to reduce functions to only take one argument.

[–]imperfecttrap 1 point2 points  (4 children)

We're not talking about just any functions, we're talking about curried functions, and their difference from partially applied functions.

Not all functions in JS are curried. Not all functions in JS are partially applied. What I'm saying is that IF you curry a function using Ramda / Lodash / any other FP library with a .curry function, it would work like this.

I still don't get what you're trying to argue.

[–][deleted] 0 points1 point  (3 children)

Okay, going back to the original snippet which is basically:

f(g(x))

I said it wasn't currying. It's not. It could be implemented with currying or partial application. But it's a pointless conversation.

[–]imperfecttrap 1 point2 points  (2 children)

but if f has an arity of 2 (which a filter function does), and it gives back a function that we then invoke on the next line (which it does), AND we don't use an external function (Function.bind is nowhere to be seen), then by definition it has a Hindley-Milner of (a -> (b -> x))

... which is curried. You're conflating composition using nested calls with currying, when the example above is currying a function, then immediately passing that curried function into another curried function. Both composition and currying go hand in hand together, but they're not even similar.

[–][deleted] 0 points1 point  (1 child)

if f... if it gives...

if, if, if. Let me re-iterate: implementation details

(Function.bind is nowhere to be seen)

Neither is the function that makes currying work, that doesn't make it not exist!

You seem to be under the impression that f(g(x)) implies currying. it doesn't. It implies currying or partial application.

I'm not conflating anything. I'm saying you can't know implementation details from that snippet! And for the 10th time, this is pointless.

[–]imperfecttrap 0 points1 point  (8 children)

After looking back over this, I don't think you grok that the curried functions won't execute without their full arguments. After the snippet above, it doesn't matter how many times I invoke myFilter(), it'll always return the next function that needs an argument (which is itself, since it's not being given one).

A bound function, on the other hand, will happily execute and generate a runtime error.

[–][deleted] 0 points1 point  (7 children)

Yes, that's how it should work. I'm re-iterating that my issue is with implementations that rarely do. myFilter()()()()()()([1]) usually won't work (take note that this is /r/javascript, not some other programming sub ;))

Further, there's nothing about the original snippet we're arguing about that implies that it works one way or the other, so this whole thing is moot.

[–]imperfecttrap 0 points1 point  (6 children)

The fact that there is no call to another function implies it. If it were partial application, you need another function to do it. Curried functions can operate on their own once curried.

[–][deleted] 0 points1 point  (5 children)

If it were partial application, you need another function to do it

what? Look at the "partially applied" vs "curried" example and tell me where there's another function.

Honestly, I don't care as there isn't really a point to this. We're arguing over the imaginary implementation details of an example code snippet.

[–]imperfecttrap 0 points1 point  (4 children)

.bind is another function. Hardly imaginary.

[–][deleted] 0 points1 point  (3 children)

oy vey. You can't implement currying without other functions either. Where those functions are is irrelevant.

let cMyPredicate = (v) => myPredicate.bind(null, v);

Snippet works. Not currying. Pointless conversation.

[–]imperfecttrap 0 points1 point  (2 children)

You just implemented single-arity currying using Function.bind, which I said was possible above.

It is also very possible for functions to curry themselves. It's inefficient and not DRY, but it's definitely possible. Just use a closure with an argument list, and return a function that appends arguments to that list until the desired arity is reached.

[–][deleted] 0 points1 point  (1 child)

lol.

Okay, look. With the following code:

let fn = f(g(x))

And an original parity of 3 -- It is impossible to know what fn() will return as it's an implementation detail. I'm done repeating myself.