Functional Destructuring: Destructure via function calls using new syntax

As mentioned up-thread, I'm currently working on a proposal for a similar feature based on Scala Extractors: Extractor Objects and Extractor Patterns for ECMAScript · GitHub

I have changes to make to better align with pattern matching (such as using Symbol.matcher instead of Symbol.apply), along with some other work to align with an ADT enum proposal, but I hope to present this to plenary later this year.

2 Likes

@rbuckton Thanks! My primary curiosity was the difference in having the "functional wrapper" on the left-hand side key vs. the value on the right side, but I see according to your actual spec, you've switched that to the right-hand side, so it always wraps the value name. Looks great to me!

It also appears this works for multiple and sub-property wrapper functions, is that right?

Foo(Bar(y)) = x; // multiple
X.Foo(y) = x; // sub-property

I really like this API :raised_hands:t3:

Then paired with the &-anywhere idea, would it work like this (below)?

const title = "Some Title";
const base &
      String.prototype.toLowerCase.apply(lower1) &
      (str => str.toLowerCase())(lower2) &
      String.prototype.toUpperCase.apply(upper1) &
      (str => str.toUpperCase())(upper2) = title;

console.log({ base, lower1, lower2, upper1, upper2 });
/*
   ->  {
         base:   "Some Title",
         lower1: "some title",
         lower2: "some title",
         upper1: "SOME TITLE",
         upper2: "SOME TITLE",
       }
*/

@bergus Per your suggestion, I've opened a proposal for the Array.prototype.ap method. Please let me know if you'd like to join me in championing that.

Thank you once again for the idea and suggested direction here.

I'd be happy to join, but I'm not a committee member.

1 Like

(A champion needs to be a TC39 delegate or invited expert)

Since, as you point out, a.ap(f) is simply sugar for a.map(x => f(x)), what is the advantage to adding it into the language?

Generally, sugar is a good thing when it avoids easy mistakes, or simplifies something that's hard to do right - I'm not sure either of those applies here.

@ljharb Speaking to my situation specifically, I was looking for a function similar to map that would allow me to apply a list of functions to a value rather than a list of values through a single function and did not so quickly/naturally reach for a .map() abstraction.

In your quoted examples above—assuming f represents an array of function—the syntax would be as follows:

f.ap(a)
/* or */
f.map((singleF) => singleF(a))

So common usage—for both syntaxes—would be:

let [a, b, c] = [getA, getB, getC].ap(obj)
/* vs. */
let [a, b, c] = [getA, getB, getC].map((fn) => fn(obj))

Agreed, maybe it's not worth introducing a new function, but having a dedicated method could improve DX.

I won't die on this hill :slightly_smiling_face:

In general, doing a one-off method from the functor hiearchy just for arrays isn't worthwhile unless you can make a good argument for its usefulness outside of functor-theoretic things. (For example, .flatMap() was good despite being an Array-specialized version of monadic bind, because filtering/expanding an array during a map is indeed a useful operation all on its own.)

In this case, I don't think the applicative map has enough real-world use-cases to justify specializing on Array, particularly since that particular specialization already has such an easy way to express it using .map(). (Again, see .flatMap() - to write it yourself required using .reduce(), which is a lot less readable.)

1 Like

Thanks, Tab! Yeah, I understand and appreciate that. When I first opened this ticket, I hadn't even considered that alternative fns(fn => fn(x)) syntax anyway, and it's not complicated as it is.

I'll go ahead and archive that repo.

Is there anything you/I/we can do to help move @rbuckton's Extractor Objects and Extractor Patterns for ECMAScript proposal forward? It solves this same problem in a more versatile and reusable way, for both the applicative use case as well as the destructuring one (instead of the alternative via approach we discussed above).

Both of these would be huge wins: