Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: @deriving for labeled arguments #6530

Open
Tracked by #6210
cometkim opened this issue Dec 14, 2023 · 3 comments
Open
Tracked by #6210

RFC: @deriving for labeled arguments #6530

cometkim opened this issue Dec 14, 2023 · 3 comments

Comments

@cometkim
Copy link
Member

cometkim commented Dec 14, 2023

Motivation

JavaScript doesn't support labeled arguments, but ReScript does. So let add: (~a, ~b) in ReScript compiled to function add(a, b), so the labels a and b cannot be referenced by the call site.

In JS/TS codebase it is common to use object destructuring for this (e.g. props in React components) Also called RORO pattern

Until v10, gentype created a runtime mapper for this. However, since v11 it no longer generates runtime mappers. In the long term, gentype shouldn't have any runtime (See #6196)

However, the runtime mapper for props is an important feature for interoperability not only with TypeScript but also with JavaScript codebases in general. There must be an alternative to completely removing the feature.

This proposal solves the problem by extending the @deriving tag existing in the ReScript core.

Detailed Design

Basic

@genType
@deriving(params)
let add = (~a, ~b) => a + b

produces

type \"add$Params" = {
  a: int,
  b: int,
}

@genType
let \"add$with" = ({ a, b }: \"add$Params") => {
  let add = add(~a, ~b)
  add
}

This is only valid for functions with labeled arguments, and requires that the @genType tag be duplicated if it exists.

High-order functions

@deriving(params) can only be used on top-level functions. This is because new type bindings cannot be defined in a closure. Btw, user may want mappers for nested functions like:

@deriving(params)
let add = (~a, ~b) => {
  (~c, ~d) => {
    (e, f) => {
      a + b + c + d + e + f
    }
  }
}

If the return type of the function annotated by @deriving(params) is a function, then it should produces

type \"add$Params" = {
  a: int,
  b: int,
}

type \"add$Params$1" = {
  c: int,
  d: int,
}

@genType
let \"add$with" = ({ a, b }: \"add$Params") => {
  let add = add(~a, ~b)
  ({ c, d }: \"add$Params$1") => {
    let add = add(~c, ~b)
    add
  }
}

Deriving is applied in nested functions, but ends when it encounters a function with no labeled parameters.

Customizing definitions

User can customize derived typenames to make it more useful in TypeScript side.

@deriving({ params: "add(Props)" })
let add = (~a, ~b) => a + b
type \"Props" = {
  a: int,
  b: int,
}

let add = ({ a, b }: \"Props") => {
  let add = add(~a, ~b)
  add
}

Implementation

TBD

@cometkim
Copy link
Member Author

cometkim commented Dec 20, 2023

@DZakh noticed another pattern related to this

The props mapping is sometimes used even within higher-order functions. But this proposal does not handle the case, for example:

let fn = (~a, ~b) => {
  (~c, ~d) => {
    a + b + c + d
  }
}

How the ppx generate types and mappers for this?


Updated the RFC to handle this case

@cometkim
Copy link
Member Author

cometkim commented Dec 20, 2023

Another issue is the genType doesn't reflect PPX's results at all. I think it should


It can be done by PPX implementor, see #6537

@cometkim
Copy link
Member Author

Implementation is currently blocked by #6539

@cometkim cometkim changed the title RFC: @deriving for labeled arguments [RFC] @deriving for labeled arguments Jan 3, 2024
@cometkim cometkim mentioned this issue Apr 2, 2024
6 tasks
@cometkim cometkim changed the title [RFC] @deriving for labeled arguments RFC: @deriving for labeled arguments Apr 2, 2024
@cknitt cknitt moved this to RFC in ReScript development Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: RFC
Development

No branches or pull requests

1 participant