RFC: New syntax for fixed external arguments (replacing `@as(...) _`)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 25/100
- issue の種類
- 機能追加
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 領域
- compilers
調査の方向性
Read the RFC’s four syntax proposals and compare their tradeoffs; the payload also points to parser and printer changes and compiler work in #8875 and #8876. Start by reviewing the existing fixed argument parser and printer behavior. Done means a syntax is chosen and its parser and printer changes are specified; this RFC is seeking design feedback, so implementation work is not yet defined.
索引モデルが issue の本文から書いたものです。
説明
Summary
External bindings sometimes need an argument that is always the same value and that callers never pass, e.g. document.createElement("img") or Object.assign({}, ...). Today such fixed arguments are written as an @as(...) attribute on a _ type:
@send external createImg: (document, @as("img") _) => element = "createElement"
@val external assign: (@as(json`{}`) _, t, {..}) => t = "Object.assign"
This RFC proposes a dedicated syntax for them and presents four options:
%raw("..."): the value as JavaScript source.=value/~label=value: the value as a ReScript literal, in "default argument" style._ = value/~label: _ = value: the value as a ReScript literal, after the_that marks the argument as not passed by callers.fixed value/~label: fixed value: the value as a ReScript literal, after a contextualfixedkeyword.
These are starting points, not a closed list: better proposals are very welcome.
Motivation
The current syntax has several problems:
- The meaning is hidden in an attribute.
@as(...)on_turns a type into a value, which is hard to discover and unlike anything else in the language.@asalso means something quite different on record fields and variant constructors. - Anything beyond strings and integers needs the special
jsontag, e.g.@as(json`{}`)for an empty object,@as(json`null`)or@as(json`false`). This madejsontemplate literals a special case in the compiler. - The value is easy to misread as a type, and the formatter and editor tooling see an attribute rather than a value.
The compiler side is being prepared independently of the syntax: #8875 gives fixed arguments their own parsetree node and removes the special json constant. #8876 adds option 1 as a provisional syntax. Whichever option this RFC settles on, it is a parser and printer change on top of that node.
Options
The examples bind document.createElement("img"), Object.assign({}, ...), history.replaceState(null, "", url) and an @obj option that is always false.
Option 1: %raw(...)
@send external createImg: (document, %raw(`"img"`)) => element = "createElement"
@val external assign: (%raw("{}"), t, {..}) => t = "Object.assign"
@val external replaceState: (%raw("null"), %raw(`""`), string) => unit = "history.replaceState"
@obj external makeOptions: (~objectMode: %raw("false"), ~name: string, unit) => options = ""
The payload is JavaScript source and must be exactly one JavaScript literal.
- Pros
- Reuses an existing construct; no new grammar.
- Clearly an escape hatch, consistent with
%rawelsewhere. - Can express any JavaScript literal, including regexps and bigints.
- Cons
- Strings need two levels of quoting (
%raw(`"img"`)). - The value is a string of JavaScript, so it gets no highlighting or formatting, and mistakes are reported late.
- Encourages raw JavaScript for what are almost always plain constants.
- Strings need two levels of quoting (
Option 2: =value and ~label=value
@send external createImg: (document, ="img") => element = "createElement"
@val external assign: (={}, t, {..}) => t = "Object.assign"
@val external replaceState: (=null, ="", string) => unit = "history.replaceState"
@obj external makeOptions: (~objectMode=false, ~name: string, unit) => options = ""
The value is a ReScript literal: string, number, boolean, null, undefined, array, object or record.
- Pros
- The labelled form mirrors the call site (
~objectMode=false). - Values are ordinary ReScript literals: no double quoting, and the compiler produces the JavaScript.
- The formatter and editor tooling treat the value like any other expression.
- The labelled form mirrors the call site (
- Cons
~x=valueis exactly the syntax of a default argument in function definitions, where the caller may still pass a different value. Here the caller cannot pass it at all.- The unlabelled form
="img"looks unusual. ~x=valueand~x: t=?side by side can be confusing.- Can't express JavaScript-only literals such as regexps.
Option 3: _ = value and ~label: _ = value
@send external createImg: (document, _ = "img") => element = "createElement"
@val external assign: (_ = {}, t, {..}) => t = "Object.assign"
@val external replaceState: (_ = null, _ = "", string) => unit = "history.replaceState"
@obj external makeOptions: (~objectMode: _ = false, ~name: string, unit) => options = ""
The values are ReScript literals, as in option 2.
- Pros
- The closest to today's syntax (
@as(x) _becomes_ = x). It keeps the_, which already says "the caller does not pass this". - The same shape for unlabelled and labelled arguments.
- Values are ordinary ReScript literals, with the same benefits as option 2.
- The closest to today's syntax (
- Cons
- Slightly more verbose than option 2.
~x: _ = valueresembles a typed default argument (~x: int=1) in function definitions.- Can't express JavaScript-only literals such as regexps.
Option 4: fixed value and ~label: fixed value
@send external createImg: (document, fixed "img") => element = "createElement"
@val external assign: (fixed {}, t, {..}) => t = "Object.assign"
@val external replaceState: (fixed null, fixed "", string) => unit = "history.replaceState"
@obj external makeOptions: (~objectMode: fixed false, ~name: string, unit) => options = ""
The values are ReScript literals, as in options 2 and 3. fixed is a contextual keyword: it only has this meaning when a value follows it, which is never valid type syntax today, so types, record fields and variables named fixed keep working.
- Pros
- Says what it means:
fixed {}reads as "this argument is always{}", with no resemblance to default arguments. - The same shape for unlabelled and labelled arguments, and reads well in both.
- Values are ordinary ReScript literals, with the same benefits as option 2.
- Easy to search for and to document.
- Says what it means:
- Cons
- Adds a (contextual) keyword to the language.
- Editor syntax highlighting needs to learn it.
- Can't express JavaScript-only literals such as regexps.
Details common to all options
- Where they're allowed. Fixed arguments are only allowed as direct arguments of an
external, inside the parenthesized argument list, soexternal f: _ = "f"keeps its current meaning. - Optional arguments. A fixed argument can't be optional, because it is always passed.
- Values in options 2 to 4. The value is read as written and isn't type-checked:
{}means an empty JavaScript object, andnull/undefinedmean the JavaScript values. - Migration.
rescript formatrewrites the existing@as(...) _forms to the new syntax automatically. Until then they keep working with a deprecation warning, and they will be removed in a later major version. The stdlib and Belt would be migrated in the same release. - PPX compatibility. PPXs keep seeing fixed arguments in the existing form,
_ [@as ...], so they aren't affected.
Feasibility
All four options have been implemented or prototyped in the parser. Option 1 is in #8876. Options 2 to 4 parse without conflicting with existing syntax, compile to the expected JavaScript, and leave the existing parser and printer test suites unchanged. For option 4, existing code using fixed as a type, field or variable name is unaffected.
Question
Which option do you prefer, and why? Or do you have a better idea?
- 主要言語
- ReScript
- スター
- 7.5k
- フォーク
- 483
- 平均マージ
- 1日 3時間
- マージ済み PR(30日)
- 136
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
rescript-lang/rescript のほかの issue
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
rescript-lang/rescript#8806 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
rescript-lang/rescript#8798 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 半日 初心者へのやさしさ 58/100
rescript-lang/rescript#8757 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 40/100
rescript-lang/rescript#8753 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 22/100
rescript-lang/rescript#8733 ·
メンテナーはふだん 1 日以内に返信
rescript-lang/rescript の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
snowballstem/snowball#311 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
JakeChampion/lang#12684 ·
メンテナーはふだん 1 日以内に返信
-
`lvm_compact` has no self-hosted twin, and `docs/158` §5 says every language-face component mustオープンbug sev-3 vm
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
-
Bisect issue233157オープンbisect
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
dtcxzyw/llvm-bisect-service#545 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信