tc39 / proposal-partial-application

Proposal to add partial application to ECMAScript
https://tc39.es/proposal-partial-application/
BSD 3-Clause "New" or "Revised" License
1.02k stars 25 forks source link

Interaction with optional chaining #35

Closed phaux closed 3 years ago

phaux commented 5 years ago

Since optional chaining is reaching stage-3, I thought it would be worth discussing how that syntax interacts with partial application. Currently it's a syntax error in babel AFAIK.

Example:

const fn = foo?.(x, ?)

Option 1

fn is a function which either does nothing if foo was nullish or in other case works normally. This would be equivalent to:

const fn = $ => foo != null ? foo(x, $) : undefined

The question is if it should also work like that with foo?.bar(?) (maybe) and foo?.bar.baz(?) (probably not)

Option 2

fn is undefined if foo was nullish. Equivalent to:

const fn = foo != null ? ($ => foo(x, $)) : undefined

It would work the same way no matter how deep in the chain which is less surprising. E.g. foo?.bar.baz(?) would be undefined if foo was nullish. We can then use the resulting maybe-function like fn?.(x).

Motivating example

Supporting this syntax would be beneficial in some situations.

const ItemList = props => (
  <ul>
    {props.items.map(item => (
      // onItemClick is optional prop and we want pass the event as second arg
      <li onClick={props.onItemClick?.(item.id, ?)}>
        {item.title}
      </li>
    ))}
  </ul>
)

This example would work pretty much the same way with both options.

phaux commented 4 years ago

Looks like babel implements option 2.

nicolo-ribaudo commented 4 years ago

Looks like babel implements option 2.

Note that this is not intended. Probably it would be better to throw an error until it's decided.

rbuckton commented 4 years ago

In general I would expect foo?.bar(?) to return undefined if foo is null/undefined, rather than deferring evaluation. The reason is that partial application eagerly evaluates non-placeholder arguments, i.e., foo.bar(++i, ?) would eagerly evaluate the pre-increment ++i. However, arguments in an optional chain are not evaluated if the root of the chain is null/undefined, i.e., foo?.bar(++i, x) would not evaluate the pre-increment ++i.

The only option to remain consistent would be Option 2.