Skip to content

Commit 38c8542

Browse files
authored
Merge branch 'main' into fix-fallback-also
2 parents f95eda7 + 2a28704 commit 38c8542

File tree

21 files changed

+1369
-80
lines changed

21 files changed

+1369
-80
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ Implementations may even support users creating their own functions.
9797

9898
See more examples and the formal definition of the grammar in [spec/syntax.md](./spec/syntax.md).
9999

100+
## Developer Documentation
101+
102+
Unofficial documentation for developers on MessageFormat 2 syntax and on using it with
103+
various programming languages can be found at [messageformat.dev](https://messageformat.dev/),
104+
which also includes an interactive [playground](https://messageformat.dev/playground/)
105+
for experimenting with message syntax.
106+
100107
## Normative Changes during Tech Preview
101108

102109
The Working Group continues to address feedback

exploration/function-composition-part-1.md

Lines changed: 225 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Function Composition
22

3-
Status: **Proposed**
3+
Status: **Obsolete**
44

55
<details>
66
<summary>Metadata</summary>
@@ -11,22 +11,20 @@ Status: **Proposed**
1111
<dd>2024-03-26</dd>
1212
<dt>Pull Requests</dt>
1313
<dd>#753</dd>
14+
<dd>#806</dd>
1415
</dl>
1516
</details>
1617

17-
## Objective
18+
## Objectives
1819

19-
_What is this proposal trying to achieve?_
20+
* Present a complete list of alternative designs for how to
21+
provide the machinery for function composition.
22+
* Create a shared vocabulary for discussing these alternatives.
2023

21-
### Non-goal
22-
23-
The objective of this design document is not to make
24-
a concrete proposal, but rather to explore a problem space.
25-
This space is complicated enough that agreement on vocabulary
26-
is desired before defining a solution.
27-
28-
Instead of objectives, we present a primary problem
29-
and a set of subsidiary problems.
24+
> [!NOTE]
25+
> This design document is preserved as part of a valuable conversation about
26+
> function composition, but it is not the basis for the design eventually
27+
> accepted.
3028
3129
### Problem statement: defining resolved values
3230

@@ -838,7 +836,10 @@ so that functions can be passed the values they need.
838836
It also needs to provide a mechanism for declaring
839837
when functions can compose with each other.
840838

841-
Other requirements:
839+
### Guarantee portability
840+
841+
A message that has a valid result in one implementation
842+
should not result in an error in a different implementation.
842843

843844
### Identify a set of use cases that must be supported
844845

@@ -975,26 +976,217 @@ Hence, revisiting the extensibility of the runtime model
975976
now that the data model is settled
976977
may result in a more workable solution.
977978

978-
## Proposed design and alternatives considered
979-
980-
These sections are omitted from this document and will be added in
981-
a future follow-up document,
982-
given the length so far and need to agree on a common vocabulary.
983-
984-
We expect that any proposed design
985-
would fall into one of the following categories:
986-
987-
1. Provide a general mechanism for custom function authors
988-
to specify how functions compose with each other.
989-
1. Specify composition rules for built-in functions,
990-
but not in general, allowing custom functions
991-
to cooperate in an _ad hoc_ way.
992-
1. Recommend a rich representation of resolved values
993-
without specifying any constraints on how these values
994-
are used.
995-
(This is the approach in [PR 645](https://github.com/unicode-org/message-format-wg/pull/645).)
996-
1. Restrict function composition for built-in functions
997-
(in order to prevent unintuitive behavior).
979+
## Alternatives to be considered
980+
981+
The goal of this section is to present a _complete_ list of
982+
alternatives that may be considered by the working group.
983+
984+
Each alternative corresponds to a different concrete
985+
definition of "resolved value".
986+
987+
## Introducing type names
988+
989+
It's useful to be able to refer to three types:
990+
991+
* `InputType`: This type encompasses strings, numbers, date/time values,
992+
all other possible implementation-specific types that input variables can be
993+
assigned to. The details are implementation-specific.
994+
* `MessageValue`: The "resolved value" type; see [PR 728](https://github.com/unicode-org/message-format-wg/pull/728).
995+
* `ValueType`: This type is the union of an `InputType` and a `MessageValue`.
996+
997+
It's tagged with a string tag so functions can do type checks.
998+
999+
```
1000+
interface ValueType {
1001+
type(): string
1002+
value(): unknown
1003+
}
1004+
```
1005+
1006+
## Alternatives to consider
1007+
1008+
In lieu of the usual "Proposed design" and "Alternatives considered" sections,
1009+
we offer some alternatives already considered in separate discussions.
1010+
1011+
Because of our constraints, implementations are **not required**
1012+
to use the `MessageValue` interface internally as described in
1013+
any of the sections.
1014+
The purpose of defining the interface is to guide implementors.
1015+
An implementation that uses different types internally
1016+
but allows the same observable behavior for composition
1017+
is compliant with the spec.
1018+
1019+
Five alternatives are presented:
1020+
1. Typed functions
1021+
2. Formatted value model
1022+
3. Preservation model
1023+
4. Allow both kinds of composition
1024+
5. Don't allow composition
1025+
1026+
### Typed functions
1027+
1028+
Types are a way for users of a language
1029+
to reason about the kinds of data
1030+
that functions can operate on.
1031+
The most ambitious solution is to specify
1032+
a type system for MessageFormat functions.
1033+
1034+
In this solution, `ValueType` is not what is defined above,
1035+
but instead is the most general type
1036+
in a system of user-defined types.
1037+
(The internal definitions are omitted.)
1038+
Using the function registry,
1039+
each custom function could declare its own argument type
1040+
and result type.
1041+
This does not imply the existence of any static typechecking.
1042+
1043+
Example B1:
1044+
```
1045+
.local $age = {$person :getAge}
1046+
.local $y = {$age :duration skeleton=yM}
1047+
.local $z = {$y :uppercase}
1048+
```
1049+
1050+
In an informal notation,
1051+
the three custom functions in this example
1052+
have the following type signatures:
1053+
1054+
```
1055+
getAge : Person -> Number
1056+
duration : Number -> String
1057+
uppercase : String -> String
1058+
```
1059+
1060+
The [function registry data model](https://github.com/unicode-org/message-format-wg/blob/main/spec/registry.md)
1061+
could be extended to define `Number` and `String`
1062+
as subtypes of `MessageValue`.
1063+
A custom function author could use the custom
1064+
registry they define to define `Person` as
1065+
a subtype of `MessageValue`.
1066+
1067+
An optional static typechecking pass (linting)
1068+
would then detect any cases where functions are composed in a way that
1069+
doesn't make sense. The advantage of this approach is documentation.
1070+
1071+
### Formatted value model (Composition operates on output)
1072+
1073+
To implement the "formatted value" model,
1074+
the `MessageValue` definition would look as in [PR 728](https://github.com/unicode-org/message-format-wg/pull/728), but without
1075+
the `resolvedOptions()` method:
1076+
1077+
```ts
1078+
interface MessageValue {
1079+
formatToString(): string
1080+
formatToX(): X // where X is an implementation-defined type
1081+
getValue(): ValueType
1082+
selectKeys(keys: string[]): string[]
1083+
}
1084+
```
1085+
1086+
`MessageValue` is effectively a `ValueType` with methods.
1087+
1088+
Using this definition would make some of the use cases
1089+
impractical. For example, the result of Example A4
1090+
might be surprising. Also, Example 1.3 from
1091+
[the dataflow composability design doc](https://github.com/unicode-org/message-format-wg/blob/main/exploration/dataflow-composability.md)
1092+
wouldn't work because options aren't preserved.
1093+
1094+
### Preservation model (Composition can operate on input and options)
1095+
1096+
In the preservation model,
1097+
functions "pipeline" the input through multiple calls.
1098+
1099+
The `ValueType` definition is different:
1100+
1101+
```ts
1102+
interface ValueType {
1103+
type(): string
1104+
value(): InputType | MessageValue
1105+
}
1106+
```
1107+
1108+
The resolved value interface would include both "input"
1109+
and "output" methods:
1110+
1111+
```ts
1112+
interface MessageValue {
1113+
formatToString(): string
1114+
formatToX(): X // where X is an implementation-defined type
1115+
getInput(): ValueType
1116+
getOutput(): ValueType
1117+
properties(): { [key: string]: ValueType }
1118+
selectKeys(keys: string[]): string[]
1119+
}
1120+
```
1121+
1122+
Compared to PR 728:
1123+
The `resolvedOptions()` method is renamed to `properties`.
1124+
Individual function implementations
1125+
choose which options to pass through into the resulting
1126+
`MessageValue`.
1127+
1128+
Instead of using `unknown` as the result type of `getValue()`,
1129+
we use `ValueType`, mentioned previously.
1130+
Instead of using `unknown` as the value type for the
1131+
`properties()` object, we use `ValueType`,
1132+
since options can also be full `MessageValue`s with their own options.
1133+
(The motivation for this is Example 1.3 from
1134+
[the "dataflow composability" design doc](https://github.com/unicode-org/message-format-wg/blob/main/exploration/dataflow-composability.md).)
1135+
1136+
This solution allows functions to pipeline input,
1137+
operate on output, or both; as well as to examine
1138+
previously passed options. Any example from this
1139+
document can be implemented.
1140+
1141+
Without a mechanism for type signatures,
1142+
it may be hard for users to tell which combinations
1143+
of functions compose without errors,
1144+
and for implementors to document that information
1145+
for users.
1146+
1147+
### Allow both kinds of composition (with different syntax)
1148+
1149+
By introducing new syntax, the same function could have
1150+
either "preservation" or "formatted value" behavior.
1151+
1152+
Consider (this suggestion is from Elango Cheran):
1153+
1154+
```
1155+
.local $x = {$num :number maxFrac=2}
1156+
.pipeline $y = {$x :number maxFrac=5 padStart=3}
1157+
{{$x} {$y}}
1158+
```
1159+
1160+
`.pipeline` would be a new keyword that acts like `.local`,
1161+
except that if its expression has a function annotation,
1162+
the formatter would apply the "preservation model" semantics
1163+
to the function.
1164+
1165+
### Don't allow composition for built-in functions
1166+
1167+
Another option is to define the built-in functions this way,
1168+
notionally:
1169+
1170+
```
1171+
number : Number -> FormattedNumber
1172+
date : Date -> FormattedDate
1173+
```
1174+
1175+
The `MessageValue` type would be defined the same way
1176+
as in the formatted value model.
1177+
1178+
The difference is that built-in functions
1179+
would not accept a "formatted result"
1180+
(would signal a runtime error in these cases).
1181+
1182+
As with the formatted value model, this restricts the
1183+
behavior of custom functions.
1184+
1185+
### Non-alternative: Allow composition in some implementations
1186+
1187+
Allow composition only if the implementation requires functions to return a resolved value as defined in [PR 728](https://github.com/unicode-org/message-format-wg/pull/728).
1188+
1189+
This violates the portability requirement.
9981190

9991191
## Acknowledgments
10001192

0 commit comments

Comments
 (0)