DEV Community

Discussion on: Currying in JavaScript

Collapse
 
jwp profile image
John Peters • Edited

See my better example below to point out how the 1 is hidden and no debugging will help determine where it came from.

Thread Thread
 
wulymammoth profile image
David • Edited

I'm not sure that I follow your example... if you've a function that reads sum, you expect it to produce a "sum". Right?

State is only relevant if it's mutable. If you supply an object with mutable state to a partially applied function, we're in for a debugging nightmare. I would even go so far as to say it's one of the reasons functional ideas have lackluster adoption in JS, unless we're dealing with scalar values or objects with immutable state.

Let's say that we have a function that helps us compute the size of a park plus some "buffer" that is only supplied at the time of construction, but we don't yet know before hand. We want to maintain a very specific interface: courts are supplied somewhere early in the process, but we expose to the builder only one parameter -- the buffer. They need not worry about courts and sizes, it is done somewhere earlier and up the chain.

function parkSize(courts) {
  return function(buffer) {
    return sum(courts) + buffer;
  }
}
Enter fullscreen mode Exit fullscreen mode

My example isn't very hard to test is it? The set-up involves two steps:

  1. supply court objects
  2. supply a buffer
  3. make an assertion

With the contrived example I have. I can "compose" a new function that exposes a "limited" and easy to understand interface for someone that needs to determine "recreational areas":

var courts = [...];
var parkArea = parkSize(courts); 
var buffer = 100; 
function recreationalSpace(buffer, privateSpace) {
  return sum(parkArea(buffer)) - privateSpace;
}
Enter fullscreen mode Exit fullscreen mode

This is no different than you using a library that's pre-configured -- you don't need to know all the default values before using it; one can infer based on the interface that's exposed. Otherwise, we could push for writing procedural imperative code that's all in a single file. How testable would that be?

Currying and partial application allow the designer/developer to control when arguments are supplied, otherwise you could end up having an outsized parameter list which forces the consumer of the function to know about the entire world at point of conception (runtime). The consumer of the function need not know about everything that was configured/supplied before it. Just like you don't need to know about all the internals of the library before you can use it. The only thing that should be implicit is the name of your function and what it's trying to convey... in the context of OOP, we this idea captured as well, called "encapsulation" -- prevent direct access to data except through a predetermined interface (methods). We don't want abstraction leaks and data access from everywhere. The consumer should need not know about the implementation details.

Every function that I've declared should be tested, so that if something is odd (like a bug), I know immediately where to look and what assumptions were made. It's really no different from us using a library that's hopefully well tested. We build tests upon a combination or composition of our own assumptions about what a library provides for us. In fact some of the libraries people use in JavaScript have functions that are exposed that are partially applied. You never need to dig into them unless seemingly unexpected behavior is exhibited after checking our own code...

Thread Thread
 
jwp profile image
John Peters • Edited

Thanks David I did some debugging today to figure this out.

 function sum(a) {
      return function (b, c) {
        return a + b + c;
      };
    }

    let mysum = sum(1);
    debugger;
    let mysum2 = mysum(2, 3);
    debugger;
Enter fullscreen mode Exit fullscreen mode

The results were:

As we can see, if a debugger wanted to know the value of mysum, in the console, they would only see it's a function as shown on far right side.

There's zero indication that this function when executed the 2nd time, has a preset value of 1 in it.

Now assume that mysum was set on entry to a module, but mysum2 was set much later. The developer is now wondering how mysum2 = 6...

mysum and mysum2 give no answers, but creating a de-composed form as shown in mysum3 kind-of, sort-of does help the debugger to understand.

This is what I was struggling with and why I mentioned an implicit state..

mysum is not a value, it's a function pointer! No values are returned until the 2nd and 3rd parameters are passed in. So this shows us that returning multiple functions forces a bit of thinking differently, as was said earlier it's composition of functions. Expressed in this syntax most clearly

let mysum3 = sum(1)(2,3);
Enter fullscreen mode Exit fullscreen mode

Indeed anonymous functions at that! I've seen this syntax before; but until now, didn't fully understand it.

Thanks!

Thread Thread
 
wulymammoth profile image
David • Edited

Ah! I love that your curiosity made you do a deeper dive! I went through something similar in other languages and honestly have never done currying or partial application in JS, although I've wanted to when I was spending most of my time in it.

Implicit state is always there, when you're working with a function that has had some arguments partially applied "earlier on". Does it make debugging more difficult? Yeah, it's not as easy as just shoving a break-point and inspecting current state. In languages, like Haskell, the lazy-evaluation becomes a challenge but yields performance gains. I work in Elixir which has nice facilities for this type of introspection, because it allows one to perform eager or lazy evaluation (Enum vs Stream) with the same functions in both modules

But I guess you're right, though -- that this idea (currying and partial application) isn't that useful in JS because it isn't the default paradigm that most of us are operating in. If employed, it would be analogous to using regexes everywhere and having to constantly explain them to everyone else. I very much like the ideas behind "functional reactive programming" ala RxJS where everything is a stream enabling some really really neat things one can do, but it is tough to get organizational buy-in when outside of some skunk-works team or small shop. But it is a powerful tool to maximize code reuse and a composition enabler in other languages and paradigms though