Hacker News new | past | comments | ask | show | jobs | submit login
Currying in JavaScript (javascript.info)
141 points by corentin88 35 days ago | hide | past | favorite | 158 comments



I've actually grown to dislike currying, it seems like a neat idea when you start using it, but whenever I come across it in real life code it just results in layers and layers of yuk - making me want to scream "show me the fucking code", you can go through 100s of lines before seeing a single operator...

I'm not a big fan of OOP either, the older I get the more I end up writing a cross between procedural and functional code with the least possible abstractions in the way - while also trying to find the smallest natural coupling between blocks of code.

Patterns are very subjective, and they usually have a place where it's objectively good to use them - but I'm not sure about currying, it feels more like a hack for when you can't do what should be done to the underlying code.


I've felt this same sort of pain but I don't blame currying. It's when currying and partial application is used for deep dependency injection with no easy route to finding the original function. I dabbled in writing an extension for F# that would trace back function calls to give you all of the possible original sources for a value. I think that would solve that particular problem.

Currying and partial application are really nice when you have a pipeline operator, which is why the partial application and pipeline operator proposals are so intertwined.

    users
    |> List.map (fn user -> user.Email)
    |> sendEmail
    |> Result.mapError (fn _ -> "Error sending emails")


> Currying and partial application are really nice when you have a pipeline operator

Currying != partial application.

And while currying is useful in curried languages (to convert back from uncurried to curried), and partial application is useful period, the question is whether currying is useful, in general, in an uncurried language.

I don't think it is:

* You usually want to perform partial application, currying is an unnecessary intermediate step.

* Currying conflicts with rich "imperative" parameters e.g. overloading, default parameters, keyword parameters.

* Uncurried languages are usually imperative and effectful, that you have filled all the parameter spots does not mean you want to invoke the function.

Having a curried language is neat, and in curried languages converting back from uncurried to curried functions is tremendously useful. But it's not so for uncurried languages (which is most of them).

> which is why the partial application and pipeline operator proposals are so intertwined.

Again, partial application != currying. And pipeline operators can perform partial application (implicitly or explicitely) on their own. For a flagrant example, just see Clojure:

    (->> users
     (map :email)
     (sendEmail)
     (mapError (constantly "Error sending emails"))


I'm not sure I fully appreciate the distinction you're trying to make here: "currying != partial application". Can you clarify?

In particular I don't know what's meant by "curried" and "uncurried" languages


In Standard ML and Haskell, all functions are curried by default -- they take a single argument, and the syntax for defining multiple-argument functions is just sugar for defining single-argument functions that return single-argument functions. That's reflected in this Haskell type signature for a function adding two integers:

   add :: Int -> Int -> Int
This is a function that takes one integer and returns a function which itself takes another integer and then finally returns an integer. Syntax sugar allows you to define it and call it like a single function.

An uncurried function in these languages is a function that takes multiple arguments as a tuple.

   add :: (Int, Int) -> Int
You don't see a lot of functions defined this way in Haskell because there isn't really any advantage to doing so in a lazy, pure language. It's more common in Standard ML from what I've seen, because SML is strict and impure.


> I'm not sure I fully appreciate the distinction you're trying to make here: "currying != partial application". Can you clarify?

Currying is the conversion of an n-ary function into a chain of 1-ary function e.g. `fn(a, b, c) -> d` becomes `fn(a) -> fn(b) -> fn(c) -> d`.

Partial application is what it says on the tin, it only applies k parameters to the function returning an (n-k) parameters function, without actually running the function.

Currying is one method allowing partial application, but not necessarily the only one, or usually the best fit as it tends to not interact well with features like overloading, default parameters, keyword parameters, …

> In particular I don't know what's meant by "curried" and "uncurried" languages

Languages like Haskell, OCaml, or Elm are "curried" by default: when you define an n-ary function it's really just sugar for a chain of 1-ary functions; and the application of multiple arguments is similarly the implicit application of single arguments multiple times e.g.

    map : (a -> b) -> List a -> List b
    map f l =
      case l of
        [] -> []
        (h::t) -> (f h) :: (map f t) 
can be called as

    map fn [1, 2, 3]
or

    (map fn) [1, 2, 3]
the semantics are identical, which is not the case in an uncurried language like Javascript or Python:

    map(fn, [1, 2, 3])
and

    map(fn)([1, 2, 3])
will behave very differently unless the implementer has taken special steps to handle this case e.g. dynamic behaviour based on an optional second parameter, overloading, etc…


> applies k parameters to the function returning an (n-k) parameters function, without actually running the function

What would be the use of this? Just delaying the execution of the function's side-effects?

I'll tip my hand: I'm working on a JS-like (and JS-targeted) language that I want to support partial-application/currying by default. Under the hood everything is represented in a curried way, but syntactically you can both define and invoke/partially-invoke functions with the more familiar syntax (commas). But this is fully superficial right now; it all happens at the parser level. Is there a downside to doing things this way?


The downside is that curried functions make default, optional, and keyword parameters difficult to implement.

For defaults/optionals, given "fn f(x, y, z=10)", if "f(1, 2)" evaluates to "f(1)(2)", does that return a function or the result of "f(1)(2)(10)"? What if you have "fn f(x, y = 10, z)"? All of this is a lot easier if you can evaluate the full argument list at the time of function invocation.

As for keyword arguments, they just don't play well with any kind of enforced ordering.


Got it.

I wonder if that ambiguity could be resolved using contextual type info? I.e. if one of the two interpretations fits the expected type, use that. Otherwise, type-error.

Maybe that would be confusing, not sure.


If you come from typical oop background like java. Imagine there is a function has 5 parameters, and you need to to provide the first 3 first. Then later on you are using this partially applied function somewhere else. You can even reuse it later with different 2 params. Compare to class, it's just like how you initialize an object with certain values, then do something else with this object.


Bagel?

I read your post yesterday. Good luck with your language!


Yes, thanks! :)

To be honest this comments section has me seriously reconsidering the "everything is partially-applicable by default" feature, haha.


At a deeper level currying is just a specific application of the Hom-tensor adjunction to the category of Sets. In particular the curried function is equivalent to the original. Partial function mappings is a restriction, and inherently not a injection. That is, generally the partial function is not in an equivalent Hom space to the original function.


そう言う事ですね〜ッ。は〜い


> Currying conflicts with rich "imperative" parameters e.g. overloading, default parameters, keyword parameters.

This doesn't contradict anything you said, but I think Idris is a language that curries everything and has default parameters.

https://docs.idris-lang.org/en/latest/tutorial/miscellany.ht...


Agreed, and I really with partial() was a builtin in Python, instead of having to fetch it from functools. partial() is useful regularly, and much more explicit/flexible than a lambda for this specific use case.


I don't see why that's better than the good old for loop.

    errors = list()
    for user in users:
        if not sendEmail(user.email)
            errors.append("Error sending emails")


For me, the functional/pipeline/call chain style changes perception from "how" to "what".

Because I say "what" do to do without going into details of "how" I feel like I have spent less mental capacity reading this portion of code.

However, it does require your transformations to be simple and composable, shove a giant multi-line lambda in there it yes, the loop will be better.


The example I gave was contrived. I frequently find myself modeling much of my logic as a series of data transformations. The more I can model logic like this, the easier it is to test.

For what it's worth, your example doesn't map 1-to-1 to the intention of what I wrote. Doing it in a more imperative style, I'd have to write

    emails = list()
    for user in users:
        emails.push(user.email)
    res = sendEmail emails
    if res.IsError:
        Error("Error sending emails")
    else:
        res
(excuse my pseudo Python)


Excuses accepted. Imperative Python would really look like:

    try:
        return sendEmail(user.email for user in users)
    except IsError:
        Error("Error sending emails") # if it does something
I agree FP pipelines are nice, but not everything needs it.


nice one, however shouldn't good old for loops be done in ALGOL instead?


Yes! Like having both pattern matching and tagged union type in a language, it is the synergy of the features that makes them significantly powerful.


Oh, the lengths people go to avoid OOP. Java:

    users.stream()
        .map(User::getEmail)
        .map(Email::send)
        .map(res -> "Error sending email: " + res)
        .collect(toList())


What's "object-oriented" about that code? I'm suspicious of vague labels like "OOP" and "FP", but even some of the Java docs call Java's streams "functional-style".

https://docs.oracle.com/javase/8/docs/api/java/util/stream/p...


the only issue with java in this context is that you'll need to create a new class/transfer object between each function call that does things.

with elixir (the previous example) you'd likely use simple hashmaps or lists like {email: "mail"} or [:error, "reason for failure"].

That doesn't sound like its amazing, but actually is in practice, because the whole language is written with that in mind (pattern matching to effectively do function/method overloading depending on the value of each argument for example)

its very concise while still being very explicit. The same would be possible with java, but you'll need to create a lot of classes/interfaces/enums/boilerplate to facilitate it.

i'd still prefer java any time at a dayjob though, because boring and dumb is generally better if you need to write code that's gonna be in use for decades... and likely going to be changed by a lot of people with varying levels of experience.


Side note to say that the previous example is actually F#, a static typed language contrary to Elixir (to be fair Elixir was heavily inspired by F# and they look similar when only looking at snippets).


> the only issue with java in this context is that you'll need to create a new class/transfer object between each function call that does things

No, in most cases you don't. The stream interface is mostly designed around simple Lists, Sets and Maps. It also interacts very well with the Collection framework (e.g. myHashmap.entrySet() yielding a set, etc.) which is part of the standard library.

You can extend streams with custom collectors, but rarely if ever you need to define intermediate data structures. You do need to define initial and terminal structures, but I'd argue that's good practice regardless.


> No, in most cases you don't

that lets you pass the result from each function to the next without explicitly stating what form they have, yes.

You'll still be missing the conciseness/explicitness because the language isn't meant to be used like that and is missing necessary features in order to facilitate it such as pattern matching by the passed in values into function.

to make a simple example, you could theoretically write the following pseudo-code

  def sendEmail({email}) do
    // send email
  end
  def sendEmail({id}) do
    // get email address
    sendEmail({email})
  end
  sendEmail({id: 244})
or to make sure your code stops executing if an error occurs (positional return values this time)

    [:ok, msg] = sendEmail(lkajsdf)


    .collect(Collectors.groupingBy(...))


that does something entirely different.

maybe actually learn to program sometimes, then you will figure out what it does.


We were hoping for similar operators in JavaScript (pipeline `|>` and partial application `?`). However TC39 just decided earlier this month that we users of the language are not worthy of such powers. This was off course meet with a lot of resentment from many JavaScript developers, including my self.

https://github.com/tc39/proposal-pipeline-operator


Considering what happened with the monadic promises, that was to be expected. I hope we'll at least have the immutable records and tuples so that the JS engine can implement them efficiently. This way JS will become an even better compilation target.


You usually want to send the e-mail in order, with a sleep in between to not overload the e-mail server, and stop at any error, so that you can fix the error and then continue from where the error was.

An error could be a malformed e-mail address, or a timeout from the e-mail server.

What you do not want is to send 10000 e-mail, then have an error like "Error sending emails", then re-run a few time, only to have some people receive 5 e-mail, and some people receive 0 e-mail.


I didn't think I had to make this clear but a lot of people seem to be getting stuck on the specifics of my example code. That example is doing nothing but showing a 5,000 foot view of what pipelines can do. Please don't take my dumb example that was written early in the morning as The One True Way of processing data, sending emails, or handling errors. It's a horrible example of that


Simplified examples always look neat. Any code/syntax will look simple if it doesn't have async, state and error handling.


I can't be bothered to type all of it out on my phone but even adding in those pieces, it doesn't change it much. This is F# so you can easily manage the async via an async computation expression, the retry logic can be encapsulated in the `sendEmails` function, and the error handling is reduced to a `Result` which is very easy to work with in a pipeline. Not everything should be done in a pipeline but pipelines make a lot of things a lot nicer


Yep. I'm reminded of the advice "Favor Composition over Inheritence", which begs the question "When can we not use Composition and need to fall back down to Inheritence?" and it turns out the answer is that you can always use composition with the exception if you are relying on someone else's library that does it wrong and you can't change it.

Dependency injection and currying both do the same thing, and they are both useless most of the time just like Inheritence. You want to avoid nesting as much as possible. We should be rejecting dependencies. We should be returning values and passing them through functional pipelines.


lmao, is this satire? We should be using whatever abstraction is easiest to reason about. Sometimes that's apeasing the primate brain with objects and things, sometimes it's shoving those things through a pipeline.

Free yourself from the dogma, just do what makes sense.


> We should be using whatever abstraction is easiest to reason about

I dare mention Rich Hickey and his talk “Simple Made Easy”, and postulate that we should be using whatever abstraction is _the simplest_ (not the easiest one) to reason about, to which the grand parent rightfully refers, as far as I understand.

Objects are easy to many, but far from being objectively simple.


I would have linked the Principle of Least Power[0], but same idea. Rich certainly fleshes the idea out with more examples and I deeply respect his opinions.

I can't find it but there once was a blog that expressed this as: Reddit is a site where you can talk about anything with the exception of a few banned topics. Voat(might have been another reddit clone?) is a much less popular site where you can talk about anything. What do they talk about on Voat? Only the few topics banned on reddit.

While is strictly more powerful than for, for is strictly more powerful than foreach, foreach is strictly more powerful than map. And yet 95% of the time, the power in map is sufficient. Therefore 95% of the time you should use map. When you encounter a foreach, you should be expecting non-purity. When you encounter a while, you know that it's doing some recursive operation that requires that power.

This let's you reason about it. This allows you to compose these less powerful things.

[0]: https://blog.codinghorror.com/the-principle-of-least-power/


Rather than that I should've written, "we should use whatever abstraction makes the problem easiest to reason about".

I have no preference for either fyi.


I'm not following. How is dependency injection like inheritance and also useless?


I think GP is saying that dependency injection is like currying, not that it's like inheritance.


"Favor composition over inheritence" is like "Favor pipelining over currying".


I'm still not getting the relationship to dependency injection. What is pipelining?


Dependency Injection is just another way to curry. In both you are passing an argument. SPBS's post[0] is very similar.

All together, "Favor composition over inheritence" is like "Favor pipelining over currying/dependency injection".

There's an example of a pipeline down the thread in p2t2p's comment[1].

[0]: https://news.ycombinator.com/item?id=28583343

[1]: https://news.ycombinator.com/item?id=28584270


But why do you have to curry to inject your dependencies? Dependency injection is satisfied by moving internal constructs in your function or method into parameters, no?


That's the style that functional programmers prefer. It makes it slightly easier to work with as you can now pass them together as one. Most OOP dependency injection is done through interfaces, but with the functional approach where you have functions as first class citizens you can just pass functions around. And as the best interface is a one function interface, that's the ideal way to do it.

The point being, essentially they are the same thing, they hold the same power, and it's just a stylistic difference.


I’m still not seeing it. As a Java dev by day, I see DI mostly through the lenses of Hollywood principle and IOC containers, neither of which bear much resemblance to currying (as I see it). When it comes to something like DI frameworks, currying is certainly not as powerful, but I don’t think that’s what you mean. Currying seems much more related to something like the builder pattern. Maybe there’s some article you know of that I could read? Googled around but didn’t find anything.


So there is two types of DI, one way automatically wired through an IOC container, and the other manually wired through constructors. They both are the same thing, but one uses some configuration and magic to make it easier to pass around these interfaces.

Mark Seeman's blog[0][1][2](many more related articles, these are particularly related) helped me grasp some of this stuff.

[0]: https://blog.ploeh.dk/2014/06/10/pure-di/

[1]: https://blog.ploeh.dk/2017/01/30/partial-application-is-depe...

[2]: https://blog.ploeh.dk/2017/01/27/dependency-injection-is-pas...


One aspect I don’t like about currying (at least with the usual syntax) is that it privileges the first parameter of each function. As soon as you want to curry on the second parameter, you lose the "natural" currying syntax and have to fall back to e.g. `half x = \x -> div x 2` or something like `half = flip div 2`. That aspect, to me, makes currying nonuniform and awkward, as in many cases the first parameter doesn’t have a particular conceptual distinction to single it out for currying. It’s a bit similar to how I don’t like Smalltalk’s asymmetric syntax for commutative operations (e.g. `x add: y`).


Agreed. I've come to view partial application as the original coder saying "I guarantee you'll never need to inspect or modify this parameter from now on." Obviously, that's rarely true in practice.

What about partial application of named parameters?


>What about partial application of named parameters?

Builder pattern. In a language that accommodates it well, such as Elm, it would look like this:

    IconLib.downloadIcon
      |> IconLib.withSize 12
      |> IconLib.withColor "red"
      |> IconLib.withCssClass "dl-icon"
Allowing currying of any parameter like:

    standardDownloadIcon = IconLib.downloadIcon
      |> IconLib.withSize 12

    largeDownloadIcon = IconLib.downloadIcon
      |> IconLib.withSize 16


You can curry the second parameter without it being too ugly like this:

  half = (`div` 2)
Also, I think the idea with curried languages is that you're supposed to write your functions with the parameter that people are most likely to want to curry first.


Of course that’s the mitigation strategy, but my point is that the usual currying syntax inherently special-cases the first parameter, and in x% of the situations that’s not what you want. From a language-design point of view it just feels awkward.


I guess that's fair. What about Scala's _ syntax, though? Does it solve the problem?


> I've actually grown to dislike currying

I believe this is a notational issue more than anything. Fixpoint style in languages like Haskell is very natural and intuitive, if you have to write a `curry` function like the article shows, you have already lost.

It's much more convenient to `ad-hoc curry` with things like:

    const f = x => myFunc(someArg, x, someOtherArg);
    return myArr.map(f).whateverElse();


Currying makes sense if haskell because it’s a curried langage, so you may need the odd uncurry, then curry it back to normal.

In uncurried langages, generic curring has few use cases. Partial application does, and some libraries may want to provide both curried and uncurried versions of some things, but generally “curry” itself is unhelpful.


What's the difference between currying and partial application/closures?


Currying is the conversion of a n-ary function to a chain of n 1-ary functions. So if you have a function `(a, b, c) => d` (a 3-function which returns a result) and you curry it it becomes a => b => c => d (a 1-fn which returns a 1-fn which return a 1-fn which returns a result).

Partial application is the application of an n-ary function to k arguments (k <= n, often but not necessarily l < n) without actually evaluating (calling) the function, so given the same `f = (a, b, c) => d`, `partial(f, 1, 2)` returns a function `c => d`.

A closure is a function which "closes over" its lexical initialisation context and carries it along.

A curried function is trivially partially applicable, just call it with less than `n` parameters and it'll yield a (also curried) function taking `n - k` parameters. It's useful to build a language on that principle, it's not really useful to provide that as a generic utility, because in most scenarios you'd rather just partially apply a function.

A closure is a completely freeform tool so it can do more or less anything you want.


The distinction sounds almost completely syntactic. I.e. foo(a, b) vs foo(a)(b). Is the former not just sugar for the latter?


> Is the former not just sugar for the latter?

Theoretically they're equivalent, but practically not really especially when you introduce more complicated "imperative" features e.g. default parameters or overloading.

They also have API impacts e.g. because Haskell is curried, its HoFs are generally of the form `fn param`, that way you can trivially partially apply the callback and use the same partially applied function to different parameters.

In method-based languages, there is no way to partially apply the callback, and in uncurried languages in general since partial application is a secondary concern you'll often see the callback last regardless so the non-callback parameters don't "get lost" at the tail end of a long callback.


Ah that makes sense about default parameters and overloading. Hmm.


Not quite. In curried languages functions often only take in ONE argument in and return ONE value out. That simplification means the language can only accept the latter form (e.g. foo(a)(b)). Surprisingly this makes the language more powerful not less by unifying all functions to one shape.

For example this allows you to write methods that only take 1 input and 1 output type arg and handle all function cases with any amount of arguments. e.g. in Java/C# you typically have lots of types/anonymous class shapes to define different function shapes to represent lambdas/functions.

e.g. C# delegate syntax (Java has a more verbose way to do this last time I checked)

delegate T Action<T>(); delegate T Func<T>(); delegate R Func<T, R>(T firstArg); delegate R Func<T1, T2, R>(T1 firstArg, T2 secondArg);

Java has things like Consumer<>, Supplier<>, Function<> etc. If you want more than a https://docs.oracle.com/javase/8/docs/api/java/util/function... you typically have to define your own template that's not standard.

However in curried languages all use cases typically are handled by one form and the compiler does the mapping for you (e.g Haskell, F#, etc).

type Func<T, R> = T -> R

The compiler translates a method like foo(a, b) to foo(a)(b) and has certain compile tricks to avoid intermediate allocations if you're specifying all the args at once.

In practice it means writing a LOT less overloads to deal with all the different argument length cases defined and a lot less "Function" types to consider. Instead of an overload in Java for example with Function, BiFunction, etc or C# with Func<>, Func<_, _>, etc you just write the one or two and chain it.


So partial takes an N-ary function and gives an M-ary function, while currying always turns an N-ary function into N unary functions, that is the main distinction?


I mostly agree but every tool has its purpose. Currying is indispensable in some composability workflows.

Consider advanced component creation in virtual dom frameworks. Components may want to yield some render responsibilities to the end-user while not exposing their inner api. They can use currying here to hide details from the end-user while providing their own public api.

I've also seen it be useful when you have sub components editing different parts of a model. You write one generic prop-assigner setModelProp(prop, val) alongside the model in the scope that owns the model. Then you give each subcomponent a curried setter with the prop locked in. Those sub-components don't have the ability to edit the model any other way now, and you haven't written a ton of boilerplate setters.


> every tool has its purpose.

That's really not true. The Y combinator is not generally useful for instance, because every modern language supports recursion.

> Currying is indispensable in some composability workflows.

It's not.

> Then you give each subcomponent a curried setter with the prop locked in.

That's partial application, not currying.

There are convenience use cases for libraries providing curried versions of some functions e.g. you might want to provide both `setModelProp(prop, val)` and `setModelProp(prop)(val)`. But that's because you expect significant use cases fo partially applied versions of the utility functions. Currying is not necessary for this, it just makes things more convenient for users.


> The Y combinator is not generally useful for instance

I wish I had been told that before trying to understand and actually use it. I once wrote a JS benchmark based on Rosetta code to compute Fibonacci numbers. Not only is the Y combinator code convoluted and quite hard to understand, it's also between 1e5...2e5 times slower than a plain iterative implementation.

Somehow I find it ironic to post such a result here.


I see, I had these two concepts conflated. I guess the difference is currying returns a function and partial application invokes the function.

In that case I don't know any indispensible situations for currying.


Ugh my team at work is obsessed with DDD for a service with two endpoints. Absolutely overkill and the levels of OO abstraction are terrible. If this were more complicated I’d understand, maybe I’m wrong in general. But writing a small Nodejs service in such a way is terrible IMO.


> I get the more I end up writing a cross between procedural and functional code with the least possible abstractions in the way - while also trying to find the smallest natural coupling between blocks of code.

Same here. I think this is a correct approach, sprinkled with some reactive code here and there (prefer reactive over other abstractions such as 2-way binding et al).


Any discussion on the utility of currying (i.e. closures) vs constructing an object with methods always reminds me of this parable:

```

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.

```

Currying (and closures) are great when you don't want to go through the overhead of constructing an entire object just to hold state. They're lightweight pseudo-objects. But the moment the number of variables you are closing over starts getting too big, it is better to formalize it into an object instead. That's how I interpret this story.


Closures and objects are both ways to pair code and data — I don't think the lesson is entirely about switching to objects when a closure becomes to large; personally, I find it to be an invitation to discover how and why code and data are paired together in the first place.

Why do closures and objects arise in such a natural fashion? I'm not entirely sure, and I don't think there's one right answer. All I can think is that I can't imagine the depth of Alan Kay's knowledge on the subject, given he pioneered scheme and smalltalk, two languages that define the core of the closure/object paradigm.


> Why do closures and objects arise in such a natural fashion

My reasoning is we need a partitioned stateful function which is impossible to achieve and the alternative are worse:

- partition global state with your own object id

- passing all state as arguments


Other ways to manage state as well:

- Managing state through a monad or effect system

- Managing state through coroutines / continuations

- Managing state through dynamic scoping (poor man's effect system)

- Associating implicitly mutating functions with types without having full objects (Rust)

Some of these are pretty powerful, especially effects and continuations. I think the reason we don't see more of them is because we haven't figured out a wholly ergonomic way to fit them into a language yet.


If you don't mind using mutation for private state in a functional language, objects and closures are kinda the exact same thing.

    (define (make-queue)
      (define data empty)
      (lambda (cmd [arg #f])
        (match cmd
          ['enqueue (set! data (append data (list arg)))]
          ['dequeue
           (define value (first data)
           (set! data (rest data))
           value])))
Don't see the connection yet? Let me try again.

    (define (make-queue)
      (define data empty)
      
      (define (instance cmd [arg #f])
        (match cmd
          ['enqueue (set! data (append data (list arg)))]
          ['dequeue
           (define value (first data)
           (set! data (rest data))
           value]))

      instance)


Slightly off topic, but later on in that Qc Na thread:

----------

This brought to mind my introduction to OOP by a talented but notoriously inarticulate programmer:

NIP: Do you know what OOP is?

Me: No.

NIP: Ah. Well, you have objects and methods. Objects: objects aren't anything. And methods are how you talk to objects. Okay?

Me: Sure.

I knew there was no point in asking questions -- this was the man at his most expository. I took him to mean, "Forget what objects are, just work with them."

-------------

Which I take to mean "Objects are black boxes. How they behave is what they are"


Who is this Qc Na master? Is that a story from a book or a legend from a forum board?


There's a bit of a tradition of writing such koans for software topics. This one is from here: http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...


there is another one, similar to this, but I believe the pupil is called grasshopper. ring any bells? ive been looking for it ...


Indeed but we can get around closures by using first class operators like bind.

This implementation I wrote is much more elegant and performant than the O(n) stack frame crud in that article:

  const curry = fn => {
    const curried = (...a) => a.length >= fn.length ? fn(...a) : curried.bind(undefined, ...a)
    return curried
  }
In fact, I can't think of any implementation in JS that would be more performant, using any paradigm, functional, object oriented, or imperative.

Try to best it if you can. I'm curious to see what HN can come up with ;-)


That's not currying, that's partial application (except bad because it forces the evaluation of the function if all parameters are filled, which is often undesirable as javascript is an eagerly evaluated language with side-effects).


The implementation in the article is technically partial application. I was just reproducing it. But you are correct. Currying is a subset of partial application. You can do currying through partial application. The function is eventually called when the correct number of parameters has finally been supplied. Currying lets you call a function, splitting it in multiple calls, providing one argument per-call. Partial Application lets you call a function, splitting it in multiple calls, providing multiple arguments per-call.

The only difference is that currying would ignore any additional parameters in a flexible language like JS.

It's easy to make this implementation only consider the first parameter. See below.

  const curry = fn => {
    const curried = (a,x) => a.length + 1 >= fn.length ? fn(...a, x) : curried.bind(undefined, [...a, x])
    return curried.bind(undefined, [])
  }

I still don't understand your point about eager execution. The function is supposed to be executed when enough parameters have been passed. I suppose you mean that JS does not check if the call arguments length equals the function signature, so strict currying is safer to avoid errors? If so, that makes sense. I tend to consider that caveat of JS a feature though, which is why partial application is what I'd use to accomplish currying. It subsumes it and provides more flexibility by allowing multiple arguments to be curried in a single call.


> I still don't understand your point about eager execution. The function is supposed to be executed when enough parameters have been passed.

Which makes perfect sense for languages which are mostly side-effect free and in which functions with no parameters do not make sense.

That's not the case of Javascript, partially applying all the parameters but not wanting to actually run the function is a useful thing to desire, and not doing that makes partial application incoherent, especially as e.g. default parameters get involved.

That is why currying is a bad fit for imperative languages, but partial application is a useful tool still.


You're not making any sense to me. Neither currying nor partial application delay running of the function once all parameters have been passed. There is no eager execution happening in the implementation I provided. I believe you misunderstand when the original function is supposed to be executed.

sum = curry((a,b,c) => a+b+c)

sum(1)(2)(3) equals 6 under currying.

sum(1,2)(3) equals 6 under partial application.

Perhaps you are thinking of calling the function with no arguments as a signal to execute the final calculation. That is NOT how currying normally works.

I believe you think currying works like this:

sum(1)(2)(3)() = 6

This is NOT standard currying. There is never a need to give an empty call to signal execution in standard currying as CS and Mathematics define it.

The implementation I provided is defacto currying by the book. Perhaps you can provide an example if we are misunderstanding eachother.


> Neither currying nor partial application delay running of the function once all parameters have been passed.

Currying does not, partial application can (and should).

> sum = curry((a,b,c) => a+b+c)

> sum(1)(2)(3) equals 6 under currying.

> sum(1,2)(3) equals 6 under partial application.

Sure. Now what happens to `sum(1, 2, 3)`? Or if the function is `(a, b, c=1) => a + b + c`?

> Perhaps you are thinking of calling the function with no arguments as a signal to execute the final calculation. That is NOT how currying normally works.

Which is the point. It's not how currying works. It is, however, how a "regular" version of partial application works e.g. Javascript's Function#bind or Python's `functools.partial`.

> I believe you are confused.

Nope.

> I believe you think currying works like this:

No.

> The implementation I provided is defacto currying by the book.

"Currying by the book" does is defined for strict arities and can not generate variable-arity functions.


Thank you for helping me understand the issue with default parameter values in a variadic language like JS, which probably makes a nonstandard currying with an execution signal more applicable.

I've created a curry which will only execute when called with no arguments, ie. sum(1)(2)(3)() = 6

This is like you mentioned, a much better suited technique of currying for a language like JS:

  const curry = fn => {
    const curried = (a,...x) => x.length === 0 ? fn(...a) : curried.bind(undefined, [...a, x[0]])
    return curried.bind(undefined, [])
  }

  // equals 15 because default c=10
  curry((a,b,c=10)=>a+b+c)(2)(3)()

We can also apply the same idea of an empty call as a signal to the multi-argument currying variation. I'd love to know what you think. And please keep reading. Because I apologize and stand corrected. Apparently partial application is single use, unlike currying. Essentially a fixed point. I have learned something today. I apologize for being so hard headed. Thank you for putting up with me and correcting me. The wikipedia article on currying confused me with regard to partial application but then I read the partial application wiki article and what you are saying clicked.

It seems like there are 3 techniques here.

Multi argument currying. Single argument currying. And partial application (aka binding)

And too many references called the former the latter, hence my confusion.


It's really no worries. At the end of the day, the issue is really that mathematical / functional / "traditional" currying is not really suited to imperative languages, which is most of them, and notions of "advanced" currying are — I think — throwing good money after bad.

Partial application is very useful (and you're correct that it's "one shot", though you can obviously layer partial applications by partially applying the result), so can the odd curried version of a library function (ideally provided by the library) be, but general-purpose currying… not so much I would say: the languages don't care for it (unlike curried language), it interacts badly with complicated parameterisation (varargs, default, keyword, …), and the almost entirely of the use cases is covered by straight partial application. What little is left can simply be curried by hand.


Currying is nice but in practice it’s rather esoteric in the JavaScript ecosystem. I’ve only seen currying being used (if at all) when transforming data with utility libraries like ramda[1]. What makes it worse it that partial application is rather cumbersome to type.

Lucky for us there is a partial application proposal, albeit still in stage 1, which makes partial application—to make functions curried—a lot easier[2].

[1] https://ramdajs.com/docs/#curry

[2] https://github.com/tc39/proposal-partial-application


Note that currying, especially with Ramda, has a very large negative performance impact.

” To improve the compositionality of our code we had defined a ton of curried functions using Ramda. But every abstraction has a cost associated with it. On profiling, we found out that the `curry` function was taking up a lot of CPU. Benchmarks show that removing curry from the code makes it up to 100 times faster. Curry function is at the core of Ramda, almost every function is curried.”

https://blog.dream11engineering.com/lessons-learned-from-run...


Wow, that is pretty awful.

I'm a pretty big fan of strong, static type systems and FP in general, and I have been writing typescript professionally for a few years now.

I decided a long time ago that trying to write Haskell in TS just isn't worth the effort.

Adopting FP ideas such as pure functions, restricting side-effects and then otherwise just using the type system to model the flow of your program goes a very long way. You don't have to write Haskell in TS to reap most of the benefits.


You can also just not use TypeScript if you want something more appropriate for functional programming styles. I know it's not always practical to switch languages, but it's equally impractical to push unergonomic, unidiomatic styles on language not built for it. There are a lot of good options to compile an FP language to JavaScript, and frankly those languages deserve more mindshare than pressing so hard on fp-ts.


What’s unergonomic or unidiomatic about TS?

As far as I understand it TS is supposed to follow as closely to JS as possible. I always saw it as “let’s add types to JS” rather than a separate language. I’m not sure I’ve seen a “this is how you write TS vs JS” formal declaration from the TS team. Am I missing something?


Say you want first-class currying and function composition. You expect this coming from any functional language and now you don't have that ability without introducing another compiler or third-party library. Do you want algebraic data types and pattern matching? You can sort of fake it, but it's certainly uglier. Do you want managed IO or immutability? Well, you can code it by convention but it's hard to enforce it and many of the libraries you consume won't be following those conventions.

In this sense, where these features are not the default and aren't first-class, will almost likely never be idiomatic because to TypeScript because there's too much friction compared to something from the ML families or the LISPs. In trying to add types to JavaScript, as a result you keep a lot of that same friction as JavaScript. I'm not saying it doesn't have a place--it's clearly super popular--but I do think it does not offer a good experience for someone looking for FP-style programming. It's really odd to see job postings with an FP team, Haskell on the back-end, and then TypeScript on the front-end... it just doesn't match, and there were other good options, even if it requires writing some libraries for these communities.


It also pollutes the call stack, making stepping through functions that much more laborious.


Wow. Was that just from the extra function instances and closures being created?


The story doesn’t tell, but looking at the Ramda source [1][2][3], it sure looks like it: each application of a curried function creates a new curried function on the fly. Didn’t check deoptigate, but I’m willing to bet V8 doesn’t optimize that well.

1: https://github.com/ramda/ramda/blob/master/source/curry.js 2: https://github.com/ramda/ramda/blob/master/source/internal/_... 3: https://github.com/ramda/ramda/blob/master/source/internal/_...


I have strong doubts that partial application will ever advance from stage 1. The reason is that partial application was supposed to compliment the pipeline operator[1].

    value |> fn(?, "static string")
However earlier this month they advanced a version of the pipeline operator with a definite topic marker which makes partial application useless in the RHS of the pipeline operator. This was of course really controversial, as you can see if you read thought the most resent issues (particularly the closed ones).

https://github.com/tc39/proposal-pipeline-operator


The partial application proposal looks reasonable and intuitive. Definitely something I would use. Is there any prior art (other languages etc.)?

Mh, it's already 4 years old. What a shame.


There’s a lot of bikeschedding on how to use it in conjunction with the (also stage 1 proposal and forever delayed) pipeline operator[1] unfortunately. Add in never ending arguments on which symbol we should use (the questionmark has a lot of responsibilities already) and here we are.

To make matters worse it appear that generally OOP style darlings such as static class blocks and true private fields seem to gain more momentum.

[1] https://github.com/tc39/proposal-pipeline-operator/wiki



Clojure has the "thread-as" macro (`as->`), but I don't think it's used much: usually thread-first (`->`) and thread-last (`->>`) will more than do the job and have less mental overhead due to their regularity.


Scala has partially applied functions that use a very similar idiom


A word of caution - using functional programming principles in a language that is not optimized for it can result in awful performance. In JavaScript, whenever you create a closure it’s a heap allocation. If you’re doing that in a tight loop, it’ll completely dominate the time it takes to run your program.


Redux is a good example of a functional pattern that makes waaay more sense in a language with pattern matching, immutable values, and ADTs built in. You have to really think about what you're doing to scale Redux up with a large app in a JS runtime and to avoid the boilerplate. Last time I used Redux with TS the typings were not particularly helpful as well but that may've improved.


Are you speaking from experience or speculating? It's intuitive that there would be some overhead, but the amount of overhead could be anywhere from tiny to huge, especially given how aggressive V8 is about optimizing certain things


A bit of both. V8 can and will optimize away closures if they can be inlined or they're unused, but it has the additional wrinkle that it's a JIT compiler and we regularly throw megabytes of compressed Javascript at it all while the user is waiting for something to happen. Not an enviable position for a compiler to be in because all analysis and optimization has to be weighed against any potential payoffs and also why I wouldn't rely on the optimizations like you could in an AOT compiled language like Rust or Haskell.

Here are some trivial examples. As you increase the N, the closure containing ones get faster, except the last one (Chrome/M1). I think in real world scenarios the benefits of the optimizations are less clear cut and also the reason why V8 has a reputation for being a bit of a memory hog which is why we should give it a hand and write dumb imperative code.

https://perf.link/#eyJpZCI6InN0NmQ4cTRrd20zIiwidGl0bGUiOiJDb...


Yes. In JS it is best to avoid FP principles such as immutability in hot code paths, the extra allocations and GCs are not worth the cleanliness.


How do functional programming languages optimises these?


I don't know about other languages, but in Kotlin these are normally inlined, which means no allocation.


Most of them, using compilers ('deforestation' optimizations etc..), and of course they also provide escape hatches for tight loops.


In my experience, currying is usually helpful as a hack and is tolerable as long as you don't get carried away. One "curry" is fine. Two+ is really pushing it and is probably a signal that something should be refactored.

A lot of people here asking "why". Only solid example of it being the more elegant solution (in my opinion) is on the front end with redux.

Maybe you have a large amount of objects in state. Each object has many properties and is used by many components. If you are using an implementation pattern that predefines selectors, you don't want tons of different functions selecting one property each. The simpler way is a single selector that "picks" the property dynamically by currying the property name to the single, more general selector.


To me the problem with this kind of explanations is that they miss the “why”. I understand “what” is currying and “how” to implement it. But I’m missing the “why”.

The real-world example doesn’t tell me why I would prefer currying instead of putting the date at the end and making it optional, then checking if missing to assign current date. And for the other argument I could create separate functions logDebug, logWarning and logError, those would call the generic function with fixed strings, right?


We use currying at work to inject dependencies. E.g.

`sendEmail(mockEmailService)(to, subject, body) `

I find it helps to differentiate between side-effecting dependencies and parameters, making code purer, easier to test, more scalable etc.


>And for the other argument I could create separate functions logDebug, logWarning and logError, those would call the generic function with fixed strings, right?

Sure, but what would you do if e.g. the importance string isn't fixed? Well, you might write a function that takes in the importance string and returns a function that calls the generic function with the dynamic string. That's currying.


Automatic, implicit currying is the Haskell feature that most surprised me with how much I like having it. It makes taking apart and composing functions so much nicer, by making a very common case for partial application syntactically and mentally cheap.

Trying to use currying in languages where it isn't built in makes no sense to me. It doesn't seem to compare well against doing the obvious partial application with a lambda. And it doesn't play well with variadic functions, default arguments, etc. (I have ideas for automatic currying of record types that I hope to explore in a toy language at some point, but that only does so much.)


It's funny, I learned about currying back when I was first learning Javascript and programming in general. That was 10 years ago, and not once in my 10 years as a professional programmer have I ever needed to curry. Maybe I've been missing out, but I don't feel like I have.


Nice explanation, however I don't really see the use case for currying yet. If you want functions with fixed arguments, you could also just create a new function that calls the other with a fixed first argument and just takes the other arguments. Am I missing something?


The most common case I’ve seen is when you want to map using 'point free' style functions, i.e. “x.map(transformItem)”[1]. Another (more) common scenario—but not yet relevant in JavaScript—is piping/composing functions[2].

[1] https://en.wikipedia.org/wiki/Tacit_programming

[2] https://github.com/tc39/proposal-pipeline-operator/wiki


> If you want functions with fixed arguments, you could also just create a new function that calls the other with a fixed first argument and just takes the other arguments.

From searching for practical applications a while back, I understood currying as a way to create a new function with fixed values rather than fixed arguments. Meaning if you had a function take arguments FIRST and SECOND, with FIRST being computationally expensive, currying would allow you to create a new function with the slower part pre-calculated.

But after brief testing I’m unsure that’s the case. It seems the examples might’ve been referring to the argument itself being a computationally expensive operation, in which case—and going back to your example—you could save the computation’s result to a variable beforehand and use that.


Currying should not create a function with anything fixed that hadn't been fixed before.


I believe you may be mixing the concept of currying with partial application.


What you're missing is that the curry() function is generic: you don't need to manually write that new function, but just type a simple oneliner using curry() to generate the function you're describing.


Defining one function that just calls another with a fixed argument is also a one-liner with arrow functions. And I would argue that this would be easier to understand and anyone reading the code wouldn’t have to understand what currying is.


I agree that the benefit is miniscule even when both the writer and the readers are familiar with currying. Which is why I personally never use curry methods to keep the cognite load low, and instead do just that.


Also useful in react. It's better to use `data-props` for performance, but if somehow `data-` props cannot contain the information (function maybe), you can use currying with previously memoize it first.

For example, if you want to pass an object to onClick event:

  const handleOnClick = _.memoize(
    (myObj) => 
      (evt) => {
        // you can do something with myObj here
    });

  // on render
  return <button onClick={handleOnClick(myObj)} >Try it</button>;
Memoize works on class component but I don't know if it works on function component.


What you describe is currying.


Isn't that more like partial application?


At that moment, Anton became enlightened.


With the main benefit of being able to also transform the argument orser or output value at the same time.


In the `logNow` example, the date argument will be eager evaluated, not lazzy evaluated. Am I right? So what was supposed to be a real use case doesn't seem useful to me.


Nice catch. Rather than date the author should've pass the DEBUG constant instead.

Or a function that return new date, but it'll make things complicated.


However , to pass a constant I could use default parameter value.


It's the function definition that's responsible for defining default parameter.

It's the function caller that's responsible for that parameter with partial application / currying / closure.

It doesn't matter if you develop both, but if you use a library you'll need partial application to define default parameter.


Cringe at the new Date() example ... will logNow() actually create a new Date() each time? no, it will use the one provided in the curry initialization.


Currying is useful, especially if you are doing point-free style functional programming or just functional programming in general. its a useful tool with a time and a place, I don't get all the "currying hate."


To be sure, the example they give is the worst they could possibly find. I man, to keep out the current date is a laudable goal for a useful log function. But to make the current date a constant is OMG. This could even be useful in a limited form when you want to issue a bunch of messages that should all point to the same moment, but even then, saying `now = Date.now(); log now, ...; log now, ...` is so much simpler and clearer.

Also you totally do not need to be able to call `f( a, b ,c )` as `f( a )( b )( c )`, rather, you should avoid the latter for reasons of clarity, simplicity, and performance. You'd be just producing anonymous single-use throwaway functions for no good reason at all. You do not want to curry when you can just `g = ( a, c ) => f( a, 'xy', c );`. This is simpler, clearer, more flexible, and more to the point. Where this is not enough, just use a class. Everyone talks about inheritance, but where classes shine is that you can bind a multitude of functionality (implemented in methods) to a single set of configuration data (`this`) (and mutable state where called for).


In my 10+ years as a JS dev, I’ve seen this pattern in numerous code bases. Inevitably it leads to bugs and confusion.

Currying can lead to code that looks simpler, but I believe it hides real complexity in a non-obvious way. Well-named wrapper functions like ‘logNow’ can be great, but arbitrary curried functions are too tricky to keep track of on teams that include juniors, backend engineers and experienced senior FE too.


I've never seen it abused, maybe just because the team cultures didn't lend themselves to that, but I have used it in one or two places myself where it was very helpful. The main one that comes to mind is pre-binding callback to be passed down to react components, so that the exact same function instance can be passed on each re-render of the parent component and the child component doesn't get triggered to render

What are some ways you've seen it abused in JS projects?


Seems like more trouble than it’s worth


... and I still don't understand why I'd want to use currying, except as a mental exercise in "look at the weird things I can do".


That's already built in to the language:

  let logNow = log.bind(null, new Date()); // [sic, "now" is fixed]
  let debugNow = logNow.bind(null, "DEBUG");
Also, uglifiers and transpilers do some horrifying stuff, this won't break if fn.length is tempered with. Unlike the article implementation.


Easier to read version:

    let logNow = (importance, message) => log(new Date(), importance, message);
    let debugNow = message => logNow("DEBUG", message);
And with this implementation, current IDEs provide a more informative auto-complete.


Wrong. Your implementation loses all parameter typing, defaulting to "any". IDEs have correct TS definitions for .bind().


That is not currying, it’s partial application.


Currying makes it implicit, but it's the same thing.

    const one = curriedSum(1)
    one(2)
is the same as

    const one = sum.bind(null, 1)
    one(2)
with the exception that the latter is explicit and not as slow as `curriedSum`.

Both `sum` and `curriedSum` can be used the same way:

    curriedSum(1)(2)
    sum.bind(null, 1)(2)
or just:

    curriedSum(1, 2)
    sum(1, 2)
One of the advantages is that non-curried functions make the return value explicit. You can't implement `sum(...addends)` with currying because curried functions have a fixed number of parameters. Once past that, a call will not return a function anymore.

    sum.bind(null, 1).bind(null, 2).bind(null, 3).bind(null, 4) // Still not called
    curriedSum(1)(2)(3)(4) // Maybe it works, maybe undefined is not a function


They're technically correct. And if you gotta write on HN, you better get the terminology correct (and that derails threads into interesting tangents often) so my bad.

Gotta say currying seems to be useless in real world. No advantage whatsoever over partial application.


Ah, TIL.

If we are being pedants. Wikipedia says:

> currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a *single argument*.

So the article isn't currying either. It's a hybrid between currying and partial application.

TBH I can't think of a practical case where you'd prefer currying over partial application. Theory/philosophy aside.


> So the article isn't currying either. It's a hybrid between currying and partial application.

Sure.

> TBH I can't think of a practical case where you'd prefer currying over partial application. Theory/philosophy aside.

For eager-evaluation uncurried language (like javascript) I would also struggle to think of one.

For curried languages, it can sometimes be useful to uncurry functions in order to make types match, after which you might have to curry them back into the "normal" form to use them.


Just because you can doesn't mean you should. Shit, for adding an array of objects with numbers just use a simple map/reduce instead of confusing future developers with your fancy curry.

Whenever I run into a curry, I can usually solve the same issue with a simple for loop that makes it instantly clear what the code does, always.


I remember feeling very excited the first time I ran into a legitimate situation, where there was no better way to do things than curry the function.


In my experience, currying is problematic in dynamically typed languages. If you accidentally pass less arguments than you should, then you'll only detect the error much later, when you try to use the result and find that it is a partially-applied function instead of whatever you were expecting.

A question for the Javascript experts: is there a way to set a property in the returned function, saying in what line of code it was created? Perhaps something like the __LINE__ macro from C...


The implementation in the submission is awfully inelegant, dated, and slow. I quickly wrote up a fast elegant ES2021 solution and tested it thoroughly. Below is the code, feel free to use it:

  const curry = fn => {
    return (...args) => {
      const curried = (...a) => {
        args = [...args]
        return args.push(...a) >= fn.length ? fn(...args) : curried
      }
      return curried()
    }
  }


I get the dated claim, but how is yours faster? It's nearly the same logic written with syntax sugar, no? The differences I see are that by using rest params instead of a deferred Array.prototype.concat, you've lifted the Array instance creation into all branches, where the original saves itself the trouble in the invariant case. And yours defines a variable whereas the original didn't. Is there some under the hood reason why this version would be more performant?

As for the inelegant claim, beauty is in the eye of the beholder. If performance is not a concern (as it won't be in most currying use-cases) I think the original version is much easier to read without years of js experience. It can be hard for newer or cross-discipline devs to understand mixed usage of rest params and spread operator since they look the same. In a scenario like this I think maintainability is a key ingredient of elegance.


The article's implementation grows the stack unnecessarily through recursively nested closures. A commenter pointed out a flaw in my code. This new implementation is even faster and more concise by doing away with the closures (other than the single inner closure):

  const curry = fn => {
    const curried = (...a) => a.length >= fn.length ? fn(...a) : curried.bind(undefined, ...a)
    return curried
  }
It uses bind which is an extremely powerful operator (a function that maps functions to functions)


Your implementation is broken for any function that has more than two arguments.

   const sum3 = curry(( a, b, c ) => a + b + c )
   const sum3_10 = sum3( 10 ) // ...
   const sum3_10_20 = sum3_10( 20 ) // ...
   sum3_10_20( 30 ) // 60
   sum3_10_20( 5 ) // 60

One of the points of currying is to be able to fork the partial application of the function at any point.


Very good catch, thank you. Here is my take on a better version that fixes the issue:

  const curry = fn => {
    const curried = (...a) => a.length >= fn.length ? fn(...a) : curried.bind(undefined, ...a)
    return curried
  }


The topic is not the implementation, but rather the concept. To make the topic accessible to as wide an audience as possible, including old C stogies, the traditional anonymous function is much easier to understand.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: