From 0d91a2dfa3b8be72c8459b1fdf6c4fad4a140c86 Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Mon, 4 Sep 2023 13:22:38 -0700 Subject: [PATCH 01/15] Design document for variable mutability and namespacing --- exploration/variable-mutability.md | 220 +++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 exploration/variable-mutability.md diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md new file mode 100644 index 0000000000..5c6076d1b4 --- /dev/null +++ b/exploration/variable-mutability.md @@ -0,0 +1,220 @@ +# Design Proposal Template + +Status: **Proposed** + +
+ Variable Namespacing and Mutability +
+
Contributors
+
@aphillips
+
First proposed
+
2023-09-04
+
Pull Request
+
#000
+
+
+ +## Objective + +_What is this proposal trying to achieve?_ + +Describe how variables are named and how externally passed variables +and internally defined variables interact. + + +## Background + +_What context is helpful to understand this proposal?_ + +- [Issue 310](https://github.com/unicode-org/message-format-wg/issues/310) + +The term **local variable** refers to a value that is defined in a declaration. + +The term **external variable** refers to a value that is passed to the +message formatter by name and which can be referred to in an expression. + +## Use-Cases + +_What use-cases do we see? Ideally, quote concrete examples._ + +- Users want to reference external variables in expressions. +- Users can modify external variables using declarations. + For example, they can perform a text transformation or assign reusable formatting options. + >``` + >let $foo = {$bar :uppercase} + >let $baz = {$someNumber :number groupingUsed=false} +- Users, such as translators, want to annotate a variable + (either local or external) without invalidating + existing use of the variable in pattern strings. For example: + >``` + >let $foo = {$foo :transform} + >match {$a :plural} {$b :plural} + >when 0 0 {...{$foo}...} + >when 0 one {...{$foo}...} + >when 0 * {...{$foo}...} + >when one 0 {...{$foo}...} + >when one one {...{$foo}...} + >when one * {...{$foo}...} + >when * 0 {...{$foo}...} + >when * one {...{$foo}...} + >when * * {...{$foo}...} + >``` +- Users want to perform multiple transforms on a value. + Since our syntax does not permit embedding or chaining, this requires multiple declarations. + >``` + >let $foo = {$foo :text-transform transform=uppercase} + >let $foo = {$foo :trim} + >let $foo = {$foo :sanitize target=html} + >``` + >This can also be achieved by renaming: + >``` + >let $foo1 = {$foo :text-transform transform=uppercase} + >let $foo2 = {$foo1 :trim} + >let $foo3 = {$foo2 :sanitize target=html} + >``` +- Users want to impose typing on (we say "annotate") external variables + or literals: + >``` + >let $fooAsNumber = {$foo :number} + >let $anotherNumber = {42 :number} + >``` +- Users may wish to provide complex annotations which are reused across mulitple patterns + >``` + >let $count = {$count :number} + >let $date = {$date :datetime dateStyle=long} + >match {$count} + >when 1 {You received one message on {$date}} + >when * {You received {$count} messages on {$date}} + >``` + + +## Requirements + +_What properties does the solution have to manifest to enable the use-cases above?_ + +- Be able to re-annotate variables without having to rename them in the message body +- Allow static analysis to detect mistakes when referencing an undefined local variable +- Be able to re-annotate variables multiple times (because we do not allow nesting) + +- _more needed?_ + +## Constraints + +_What prior decisions and existing conditions limit the possible design?_ + +- Variable names are potentially contrained by `Nmtoken`. + The reason we chose Nmtoken (and Name) was to maximize compatibility with (potential) + LDML constructs, since CLDR uses XML. + The "carve out" for various sigils doesn't conflict with naming in LDML currently and + future conflicts are under our (CLDR-TC's) control. + +## Proposed Design + +_Describe the proposed solution. Consider syntax, formatting, errors, registry, tooling, interchange._ + +Separate local variables from externally passed values by altering the sigil +and by using a visually distinctive pattern for local names +(in an effort to prevent `$foo`/`@foo` confusion). + +```abnf +variable = local_var / external_var +local_var = "@_" name +external_var = "$" name +``` + +> *Example* +> ``` +> let @_local = {$external :transform} +> let @_anotherLocalVar = {|Some literal| :annotated} +> ``` + +To allow users to perform multiple annotations on a value, +while still allowing detection of unintentional reassignment, +introduce a new keyword `modify`: + +```abnf +declaration = (let / modify) s variable [s] "=" [s] expression +... +modify = %6D.%6F.%64.%69.%66.%79 +``` + +It is a syntax error to use `let` on a variable that has been previously +assigned through any declaration (either `let` or `modify`) + +It is a variable resolution error to call `modify` +on an external variable that does not exist. + +> *Example* +> ``` +> let @_local = {$external :transform} +> modify @_local = {@_local :modification with=options} +> modify $external = {$external :transform adding=annotation} +> ``` + +The choice here of `@_` as the local variable sigil is probably not distinctive enough. +It is probably okay to be a little inconvenient with local variable naming +as these are less common than external variables. +Alternatives to consider: + +- `@@foo` +- `@foo@` +- `@!foo` +- `@ONLY_UPPER_ASCII_SNAKE` + +Note: if we have separate namespaces then local variables don't +require Unicode names because their namespace is not subject +to external data requirements. + +## Alternatives Considered + +_What other solutions are available?_ +_How do they compare against the requirements?_ +_What other properties they have?_ + +### All Variables are Mutable; Shared Namespace + +**This is the current design.** +A declaration can overwrite any passed in (external) value, +either by adding annotation +or by completely replacing the value. +Further, one declaration can modify or completely overwrite a previous annotation. + +There are no warnings or errors produced when this occurs, even when it is unintentional. + +If variables are mutable and namespaces are shared, it's easy to write a message that never +fails but does produce unintended or unexpected results (from the caller's point of view). +``` +{"arg1": "10000"} +... +let $arg1 = {42} +{This always says {$arg1} == 42} +``` + +### All Variables are Mutable; Non-shared Namespace +If variables are mutable but namespaces are not shared, its easy for developers or translators to reference the wrong one: +``` +{"arg1": "10000"} +... +let #arg1 = {42 :number maxFractionDigits=2} +{This always says {$arg1} == 10000 because it should say {#arg1}} +``` + +### All Variables are Immutable; Shared Namespace + +If we make all variables immutable and external and local vars share a namespace, +passing an argument that shares a name with a local declaration can cause a message to fail. +``` +{ "arg1": "37"} +... +let $arg1 = {|42| :number maxFractionDigits=2} // error +``` + +### All Variables are Immutable; Non-shared Namespace +If we make all variables immutable but external and local vars do not share a namespace, +this problem goes away. However, a local variable cannot be used to augment or annotate an external variable. +``` +{ "arg1": "42" } +... +let #arg1 = {$arg1 :number maxFractionDigits=2} +{Now I have to change {$arg} to {#arg}...} +``` From 296c87b442d9ce1b9d11682fe61d8b6ec229c29c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:22:57 +0000 Subject: [PATCH 02/15] style: Apply Prettier --- exploration/variable-mutability.md | 109 ++++++++++++++++------------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 5c6076d1b4..2477941d16 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -21,7 +21,6 @@ _What is this proposal trying to achieve?_ Describe how variables are named and how externally passed variables and internally defined variables interact. - ## Background _What context is helpful to understand this proposal?_ @@ -40,53 +39,55 @@ _What use-cases do we see? Ideally, quote concrete examples._ - Users want to reference external variables in expressions. - Users can modify external variables using declarations. For example, they can perform a text transformation or assign reusable formatting options. - >``` - >let $foo = {$bar :uppercase} - >let $baz = {$someNumber :number groupingUsed=false} + > ``` + > let $foo = {$bar :uppercase} + > let $baz = {$someNumber :number groupingUsed=false} + > ``` - Users, such as translators, want to annotate a variable (either local or external) without invalidating existing use of the variable in pattern strings. For example: - >``` - >let $foo = {$foo :transform} - >match {$a :plural} {$b :plural} - >when 0 0 {...{$foo}...} - >when 0 one {...{$foo}...} - >when 0 * {...{$foo}...} - >when one 0 {...{$foo}...} - >when one one {...{$foo}...} - >when one * {...{$foo}...} - >when * 0 {...{$foo}...} - >when * one {...{$foo}...} - >when * * {...{$foo}...} - >``` + > ``` + > let $foo = {$foo :transform} + > match {$a :plural} {$b :plural} + > when 0 0 {...{$foo}...} + > when 0 one {...{$foo}...} + > when 0 * {...{$foo}...} + > when one 0 {...{$foo}...} + > when one one {...{$foo}...} + > when one * {...{$foo}...} + > when * 0 {...{$foo}...} + > when * one {...{$foo}...} + > when * * {...{$foo}...} + > ``` - Users want to perform multiple transforms on a value. Since our syntax does not permit embedding or chaining, this requires multiple declarations. - >``` - >let $foo = {$foo :text-transform transform=uppercase} - >let $foo = {$foo :trim} - >let $foo = {$foo :sanitize target=html} - >``` - >This can also be achieved by renaming: - >``` - >let $foo1 = {$foo :text-transform transform=uppercase} - >let $foo2 = {$foo1 :trim} - >let $foo3 = {$foo2 :sanitize target=html} - >``` + > ``` + > let $foo = {$foo :text-transform transform=uppercase} + > let $foo = {$foo :trim} + > let $foo = {$foo :sanitize target=html} + > ``` + > + > This can also be achieved by renaming: + > + > ``` + > let $foo1 = {$foo :text-transform transform=uppercase} + > let $foo2 = {$foo1 :trim} + > let $foo3 = {$foo2 :sanitize target=html} + > ``` - Users want to impose typing on (we say "annotate") external variables or literals: - >``` - >let $fooAsNumber = {$foo :number} - >let $anotherNumber = {42 :number} - >``` + > ``` + > let $fooAsNumber = {$foo :number} + > let $anotherNumber = {42 :number} + > ``` - Users may wish to provide complex annotations which are reused across mulitple patterns - >``` - >let $count = {$count :number} - >let $date = {$date :datetime dateStyle=long} - >match {$count} - >when 1 {You received one message on {$date}} - >when * {You received {$count} messages on {$date}} - >``` - + > ``` + > let $count = {$count :number} + > let $date = {$date :datetime dateStyle=long} + > match {$count} + > when 1 {You received one message on {$date}} + > when * {You received {$count} messages on {$date}} + > ``` ## Requirements @@ -122,7 +123,8 @@ local_var = "@_" name external_var = "$" name ``` -> *Example* +> _Example_ +> > ``` > let @_local = {$external :transform} > let @_anotherLocalVar = {|Some literal| :annotated} @@ -141,10 +143,11 @@ modify = %6D.%6F.%64.%69.%66.%79 It is a syntax error to use `let` on a variable that has been previously assigned through any declaration (either `let` or `modify`) -It is a variable resolution error to call `modify` +It is a variable resolution error to call `modify` on an external variable that does not exist. -> *Example* +> _Example_ +> > ``` > let @_local = {$external :transform} > modify @_local = {@_local :modification with=options} @@ -161,9 +164,9 @@ Alternatives to consider: - `@!foo` - `@ONLY_UPPER_ASCII_SNAKE` -Note: if we have separate namespaces then local variables don't +Note: if we have separate namespaces then local variables don't require Unicode names because their namespace is not subject -to external data requirements. +to external data requirements. ## Alternatives Considered @@ -173,16 +176,17 @@ _What other properties they have?_ ### All Variables are Mutable; Shared Namespace -**This is the current design.** -A declaration can overwrite any passed in (external) value, +**This is the current design.** +A declaration can overwrite any passed in (external) value, either by adding annotation or by completely replacing the value. Further, one declaration can modify or completely overwrite a previous annotation. There are no warnings or errors produced when this occurs, even when it is unintentional. -If variables are mutable and namespaces are shared, it's easy to write a message that never +If variables are mutable and namespaces are shared, it's easy to write a message that never fails but does produce unintended or unexpected results (from the caller's point of view). + ``` {"arg1": "10000"} ... @@ -191,7 +195,9 @@ let $arg1 = {42} ``` ### All Variables are Mutable; Non-shared Namespace + If variables are mutable but namespaces are not shared, its easy for developers or translators to reference the wrong one: + ``` {"arg1": "10000"} ... @@ -201,8 +207,9 @@ let #arg1 = {42 :number maxFractionDigits=2} ### All Variables are Immutable; Shared Namespace -If we make all variables immutable and external and local vars share a namespace, +If we make all variables immutable and external and local vars share a namespace, passing an argument that shares a name with a local declaration can cause a message to fail. + ``` { "arg1": "37"} ... @@ -210,8 +217,10 @@ let $arg1 = {|42| :number maxFractionDigits=2} // error ``` ### All Variables are Immutable; Non-shared Namespace -If we make all variables immutable but external and local vars do not share a namespace, + +If we make all variables immutable but external and local vars do not share a namespace, this problem goes away. However, a local variable cannot be used to augment or annotate an external variable. + ``` { "arg1": "42" } ... From da944d02e18b84208cc62f80c869b2028ddc0d9a Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Mon, 4 Sep 2023 13:58:03 -0700 Subject: [PATCH 03/15] Partly address #299 --- exploration/variable-mutability.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 2477941d16..e661933174 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -89,6 +89,8 @@ _What use-cases do we see? Ideally, quote concrete examples._ > when * {You received {$count} messages on {$date}} > ``` +- Implementers need to know what value is associated with a named variable, see #299. + ## Requirements _What properties does the solution have to manifest to enable the use-cases above?_ @@ -154,6 +156,18 @@ on an external variable that does not exist. > modify $external = {$external :transform adding=annotation} > ``` +When more than one `modify` declaration applies to the same named variable, +or when a `modify` declaration is applied to a local variable +defined in a `let` declaration, +the named variable behaves as if each declaration were called +in the sequence in which they appear in the message. +Implementations are not required (by this design, anyway) +to resolve values in a greedy manner. +They might not resolve a value unless it is actually used in a selector +or in a placeholder. + +#### Sigil Choice for Local Variables + The choice here of `@_` as the local variable sigil is probably not distinctive enough. It is probably okay to be a little inconvenient with local variable naming as these are less common than external variables. From aeb1df98911887dc2571f64d3b12f10ebe13af25 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:58:22 +0000 Subject: [PATCH 04/15] style: Apply Prettier --- exploration/variable-mutability.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index e661933174..8c37a28765 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -81,6 +81,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ > let $anotherNumber = {42 :number} > ``` - Users may wish to provide complex annotations which are reused across mulitple patterns + > ``` > let $count = {$count :number} > let $date = {$date :datetime dateStyle=long} @@ -158,11 +159,11 @@ on an external variable that does not exist. When more than one `modify` declaration applies to the same named variable, or when a `modify` declaration is applied to a local variable -defined in a `let` declaration, +defined in a `let` declaration, the named variable behaves as if each declaration were called in the sequence in which they appear in the message. Implementations are not required (by this design, anyway) -to resolve values in a greedy manner. +to resolve values in a greedy manner. They might not resolve a value unless it is actually used in a selector or in a placeholder. From 595bb0b8a5ebf75178e079559e058b80f64115df Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Mon, 4 Sep 2023 14:54:33 -0700 Subject: [PATCH 05/15] Address comments, fix sigil choice - change `@` to `#` because we want to use `@` for annotations such as `@locale` - Provide text that considers not making ugly local variables - Provide use cases for static analysis - Call out the perfidy of the author in stealing ill-baked requirements --- exploration/variable-mutability.md | 47 +++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 8c37a28765..0e10b9bb3b 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -43,9 +43,14 @@ _What use-cases do we see? Ideally, quote concrete examples._ > let $foo = {$bar :uppercase} > let $baz = {$someNumber :number groupingUsed=false} > ``` + - Users, such as translators, want to annotate a variable (either local or external) without invalidating - existing use of the variable in pattern strings. For example: + existing use of the variable in pattern strings. + This saves the effort of finding and fixing all occurences + in the various pattern strings, as well as issues that could arise from + (for example) translation memory systems recalling the old expression. + For example: > ``` > let $foo = {$foo :transform} > match {$a :plural} {$b :plural} @@ -59,6 +64,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ > when * one {...{$foo}...} > when * * {...{$foo}...} > ``` + - Users want to perform multiple transforms on a value. Since our syntax does not permit embedding or chaining, this requires multiple declarations. > ``` @@ -74,14 +80,14 @@ _What use-cases do we see? Ideally, quote concrete examples._ > let $foo2 = {$foo1 :trim} > let $foo3 = {$foo2 :sanitize target=html} > ``` -- Users want to impose typing on (we say "annotate") external variables - or literals: + +- Users want to annotate external variables or literals: > ``` > let $fooAsNumber = {$foo :number} > let $anotherNumber = {42 :number} > ``` -- Users may wish to provide complex annotations which are reused across mulitple patterns +- Users may wish to provide complex annotations which are reused across mulitple patterns > ``` > let $count = {$count :number} > let $date = {$date :datetime dateStyle=long} @@ -92,10 +98,19 @@ _What use-cases do we see? Ideally, quote concrete examples._ - Implementers need to know what value is associated with a named variable, see #299. +- Users would like their tooling to identify, perhaps via static analysis, when + they have mistyped or used an undeclared local variable. + +- Users would like to be able to create local variables without accidentially + overwriting external values. (The inverse of this, in which the declaration + overwrites an external value, can be difficult to debug if it occurs in, + for example, just one of many different localized string variations.) + ## Requirements _What properties does the solution have to manifest to enable the use-cases above?_ +These were taken from a comment by @stasm in #310: - Be able to re-annotate variables without having to rename them in the message body - Allow static analysis to detect mistakes when referencing an undefined local variable - Be able to re-annotate variables multiple times (because we do not allow nesting) @@ -122,15 +137,15 @@ and by using a visually distinctive pattern for local names ```abnf variable = local_var / external_var -local_var = "@_" name +local_var = "#_" name external_var = "$" name ``` > _Example_ > > ``` -> let @_local = {$external :transform} -> let @_anotherLocalVar = {|Some literal| :annotated} +> let #_local = {$external :transform} +> let #_anotherLocalVar = {|Some literal| :annotated} > ``` To allow users to perform multiple annotations on a value, @@ -152,8 +167,8 @@ on an external variable that does not exist. > _Example_ > > ``` -> let @_local = {$external :transform} -> modify @_local = {@_local :modification with=options} +> let #_local = {$external :transform} +> modify #_local = {#_local :modification with=options} > modify $external = {$external :transform adding=annotation} > ``` @@ -169,20 +184,24 @@ or in a placeholder. #### Sigil Choice for Local Variables -The choice here of `@_` as the local variable sigil is probably not distinctive enough. +The choice here of `#_` as the local variable sigil is probably not distinctive enough. It is probably okay to be a little inconvenient with local variable naming as these are less common than external variables. Alternatives to consider: -- `@@foo` -- `@foo@` -- `@!foo` -- `@ONLY_UPPER_ASCII_SNAKE` +- `##foo` +- `#foo#` +- `#!foo` +- `#ONLY_UPPER_ASCII_SNAKE` Note: if we have separate namespaces then local variables don't require Unicode names because their namespace is not subject to external data requirements. +A different option is to say: it is up to the user to avoid using +declared names that would confuse translators and others. +This would mean that we provide no defense on the syntax level. + ## Alternatives Considered _What other solutions are available?_ From 3cb687bd45cc4a3ba99cf30cad6595a14e54f9f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:54:51 +0000 Subject: [PATCH 06/15] style: Apply Prettier --- exploration/variable-mutability.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 0e10b9bb3b..745c930f9b 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -39,6 +39,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ - Users want to reference external variables in expressions. - Users can modify external variables using declarations. For example, they can perform a text transformation or assign reusable formatting options. + > ``` > let $foo = {$bar :uppercase} > let $baz = {$someNumber :number groupingUsed=false} @@ -51,6 +52,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ in the various pattern strings, as well as issues that could arise from (for example) translation memory systems recalling the old expression. For example: + > ``` > let $foo = {$foo :transform} > match {$a :plural} {$b :plural} @@ -67,6 +69,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ - Users want to perform multiple transforms on a value. Since our syntax does not permit embedding or chaining, this requires multiple declarations. + > ``` > let $foo = {$foo :text-transform transform=uppercase} > let $foo = {$foo :trim} @@ -82,12 +85,14 @@ _What use-cases do we see? Ideally, quote concrete examples._ > ``` - Users want to annotate external variables or literals: + > ``` > let $fooAsNumber = {$foo :number} > let $anotherNumber = {42 :number} > ``` - Users may wish to provide complex annotations which are reused across mulitple patterns + > ``` > let $count = {$count :number} > let $date = {$date :datetime dateStyle=long} @@ -111,6 +116,7 @@ _What use-cases do we see? Ideally, quote concrete examples._ _What properties does the solution have to manifest to enable the use-cases above?_ These were taken from a comment by @stasm in #310: + - Be able to re-annotate variables without having to rename them in the message body - Allow static analysis to detect mistakes when referencing an undefined local variable - Be able to re-annotate variables multiple times (because we do not allow nesting) @@ -198,7 +204,7 @@ Note: if we have separate namespaces then local variables don't require Unicode names because their namespace is not subject to external data requirements. -A different option is to say: it is up to the user to avoid using +A different option is to say: it is up to the user to avoid using declared names that would confuse translators and others. This would mean that we provide no defense on the syntax level. From 11f85b045dad45c534be5fec345a0e2132150bac Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Sat, 9 Sep 2023 12:15:14 +0200 Subject: [PATCH 07/15] Add @eemelie's `input` proposal as an option considered --- exploration/variable-mutability.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 745c930f9b..0ae12cee78 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -267,3 +267,22 @@ this problem goes away. However, a local variable cannot be used to augment or a let #arg1 = {$arg1 :number maxFractionDigits=2} {Now I have to change {$arg} to {#arg}...} ``` + +### All Variables are Immutable; Externals are Annotatable; Shared Namespace + +_This is @eemelie's proposal from the PR thread_ + +If we add a new reserved keyword (the proposal says `input`) to +allow annotation of external variables, and allow `let` to mask +external values. + +``` +[ {"arg1": "37"}, + {"arg2": "42"} +] +... +input {$arg1 :number minFractionDigits=2} +let $arg2 = {|wildebeest|} +{{$arg1} prints 37.00 and {$arg2} prints wildebeest, no errors} +... +``` From e7cda16ff16c9260538e5749f2cefc5e8c725972 Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Sat, 9 Sep 2023 14:34:07 +0200 Subject: [PATCH 08/15] Update exploration/variable-mutability.md Co-authored-by: Eemeli Aro --- exploration/variable-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 0ae12cee78..a074b4a9eb 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -270,7 +270,7 @@ let #arg1 = {$arg1 :number maxFractionDigits=2} ### All Variables are Immutable; Externals are Annotatable; Shared Namespace -_This is @eemelie's proposal from the PR thread_ +_This is @eemeli's proposal from the PR thread_ If we add a new reserved keyword (the proposal says `input`) to allow annotation of external variables, and allow `let` to mask From 152b73c94472d3c168abb9ef2606e947e5e6bef4 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 12 Sep 2023 11:17:16 +0200 Subject: [PATCH 09/15] Add new proposed design --- exploration/variable-mutability.md | 128 +++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 26 deletions(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index a074b4a9eb..75f9369d8a 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -135,7 +135,108 @@ _What prior decisions and existing conditions limit the possible design?_ ## Proposed Design -_Describe the proposed solution. Consider syntax, formatting, errors, registry, tooling, interchange._ +Let us introduce a new keyword `input` that allows for the annotation of external variables. +It works like this: + +``` +input {$count :number} +input {$date :datetime dateStyle=long} +match {$count} +when 1 {You received one message on {$date}} +when * {You received {$count} messages on {$date}} +``` + +This effectively replaces the current "hack" of saying `let $foo = {$foo ...}`, +and provides a way to explicitly declare that a variable is non-local: +`input {$bar}` doesn't require an annotation. + +To correspond with "input", +let us also change the local variable declaration keyword to be `local` rather than `let`. + +At the syntax/data model level, +`input` or `local` declarations would not be required for all selectors and placeholders, +but a user-configured validator could of course be stricter. + +In the ABNF the change would look like this: + +```abnf +message = [s] *(declaration [s]) body [s] +declaration = input-declaration / local-declaration + +input-declaration = input [s] "{" [s] variable [s annotation] [s] "}" +input = %x69.6E.70.75.74 ; "input" + +local-declaration = local s variable [s] "=" [s] expression +local = %x6C.6F.63.61.6C ; "local" +``` + +The _expression_ rule can't be used directly in _input-declaration_ because the _variable_ is required. + +With this approach, variables are immutable, +so each may be defined by only one _declaration_. +The order of declarations does not matter, +to allow for e.g. `input` annotations to refer to `local` variables. + +References to later declarations are not allowed, +so this is considered an error: + +``` +local $foo = {$bar :number} +local $bar = {42 :number} +{The answer is {$foo}} +``` + +An _input-declaration_ is not required for each external variable. +A _local-declaration_ takes precedence and does not cause an error +if a similarly named external variable is passed to the formatter +_without_ a corresponding _input-declaration_ in the message. + +The use case of chaining operations on a variable with a single name is not supported here, +and the `$foo1`, `$foo2` `$foo3` sorts of names would be required for that. + +> The examples given above would be written as follows: +> +> ``` +> local $foo = {$bar :uppercase} +> local $baz = {$someNumber :number groupingUsed=false} +> ``` +> +> ``` +> input {$foo :transform} +> match {$a :plural} {$b :plural} +> when 0 0 {...{$foo}...} +> when 0 one {...{$foo}...} +> when 0 * {...{$foo}...} +> when one 0 {...{$foo}...} +> when one one {...{$foo}...} +> when one * {...{$foo}...} +> when * 0 {...{$foo}...} +> when * one {...{$foo}...} +> when * * {...{$foo}...} +> ``` +> +> ``` +> input {$foo :text-transform transform=uppercase} +> local $foo2 = {$foo :trim} +> local $foo3 = {$foo2 :sanitize target=html} +> ``` +> +> ``` +> local $fooAsNumber = {$foo :number} +> local $anotherNumber = {42 :number} +> ``` +> +> ``` +> input {$count :number} +> input {$date :datetime dateStyle=long} +> match {$count} +> when 1 {You received one message on {$date}} +> when * {You received {$count} messages on {$date}} +> ``` + +## Alternatives Considered + +### Original Proposal Separate local variables from externally passed values by altering the sigil and by using a visually distinctive pattern for local names @@ -208,12 +309,6 @@ A different option is to say: it is up to the user to avoid using declared names that would confuse translators and others. This would mean that we provide no defense on the syntax level. -## Alternatives Considered - -_What other solutions are available?_ -_How do they compare against the requirements?_ -_What other properties they have?_ - ### All Variables are Mutable; Shared Namespace **This is the current design.** @@ -267,22 +362,3 @@ this problem goes away. However, a local variable cannot be used to augment or a let #arg1 = {$arg1 :number maxFractionDigits=2} {Now I have to change {$arg} to {#arg}...} ``` - -### All Variables are Immutable; Externals are Annotatable; Shared Namespace - -_This is @eemeli's proposal from the PR thread_ - -If we add a new reserved keyword (the proposal says `input`) to -allow annotation of external variables, and allow `let` to mask -external values. - -``` -[ {"arg1": "37"}, - {"arg2": "42"} -] -... -input {$arg1 :number minFractionDigits=2} -let $arg2 = {|wildebeest|} -{{$arg1} prints 37.00 and {$arg2} prints wildebeest, no errors} -... -``` From 24c9c732d9af9a04528b3722e101772e240f7f53 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 12 Sep 2023 19:23:52 +0300 Subject: [PATCH 10/15] Update exploration/variable-mutability.md Co-authored-by: Addison Phillips --- exploration/variable-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 75f9369d8a..bd171c57fa 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -188,7 +188,7 @@ local $bar = {42 :number} An _input-declaration_ is not required for each external variable. A _local-declaration_ takes precedence and does not cause an error -if a similarly named external variable is passed to the formatter +if an identically named external variable is passed to the formatter _without_ a corresponding _input-declaration_ in the message. The use case of chaining operations on a variable with a single name is not supported here, From 2c81fb0321ee4708930b44892492ee71f8aa835b Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Wed, 4 Oct 2023 13:28:56 -0700 Subject: [PATCH 11/15] Address @eemeli's comments Specifically the one about forward references --- exploration/variable-mutability.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index bd171c57fa..60994ca91e 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -174,8 +174,6 @@ The _expression_ rule can't be used directly in _input-declaration_ because the With this approach, variables are immutable, so each may be defined by only one _declaration_. -The order of declarations does not matter, -to allow for e.g. `input` annotations to refer to `local` variables. References to later declarations are not allowed, so this is considered an error: @@ -186,6 +184,13 @@ local $bar = {42 :number} {The answer is {$foo}} ``` +Note that this means that `input` declarations can (and sometimes _must_) +follow `local` ones, such as when an `input` is annotated using a `local` value: +``` +local $foo = {|2| :number} +input $bar :number maxFractionDigits={$foo} +``` + An _input-declaration_ is not required for each external variable. A _local-declaration_ takes precedence and does not cause an error if an identically named external variable is passed to the formatter From 53768849a91ed8ea9e2fd857a9de8d367fa64056 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 20:29:18 +0000 Subject: [PATCH 12/15] style: Apply Prettier --- exploration/variable-mutability.md | 1 + 1 file changed, 1 insertion(+) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 60994ca91e..bec492c7d2 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -186,6 +186,7 @@ local $bar = {42 :number} Note that this means that `input` declarations can (and sometimes _must_) follow `local` ones, such as when an `input` is annotated using a `local` value: + ``` local $foo = {|2| :number} input $bar :number maxFractionDigits={$foo} From 0bbf851def1ae2548a36eab3fa5e3b3725271cbb Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Sun, 8 Oct 2023 08:53:23 -0700 Subject: [PATCH 13/15] Update exploration/variable-mutability.md Co-authored-by: Eemeli Aro --- exploration/variable-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index bec492c7d2..1f3503369f 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -10,7 +10,7 @@ Status: **Proposed**
First proposed
2023-09-04
Pull Request
-
#000
+
#469
From da848ad9f7ba9979a71477b3d317d27f3493dee6 Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Sun, 8 Oct 2023 08:53:56 -0700 Subject: [PATCH 14/15] Update exploration/variable-mutability.md Co-authored-by: Eemeli Aro --- exploration/variable-mutability.md | 1 + 1 file changed, 1 insertion(+) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 1f3503369f..47f2bc766e 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -7,6 +7,7 @@ Status: **Proposed**
Contributors
@aphillips
+
@eemeli
First proposed
2023-09-04
Pull Request
From fb3da4c670c71fcee6a5551ef4f04ad9489ac408 Mon Sep 17 00:00:00 2001 From: Addison Phillips Date: Sun, 8 Oct 2023 08:54:07 -0700 Subject: [PATCH 15/15] Update exploration/variable-mutability.md Co-authored-by: Eemeli Aro --- exploration/variable-mutability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exploration/variable-mutability.md b/exploration/variable-mutability.md index 47f2bc766e..c7e324905f 100644 --- a/exploration/variable-mutability.md +++ b/exploration/variable-mutability.md @@ -1,6 +1,6 @@ # Design Proposal Template -Status: **Proposed** +Status: **Accepted**
Variable Namespacing and Mutability