unicode-org / message-format-wg

Developing a standard for localizable message strings
Other
236 stars 34 forks source link

[FEEDBACK] What is the semantic difference between local declaration using itself, and an input declaration #819

Open lucacasonato opened 4 months ago

lucacasonato commented 4 months ago

Consider the following message:

.input {$count :integer}
{{You have {$count} apples.}}

From my understanding this would be identical to the following message, were it allowed.

.local $count = {$count :integer}
{{You have {$count} apples.}}

I understand this is not allowed, due to the following syntax restriction:

A local-declaration MUST NOT bind a variable that appears in its expression.

This does make me wonder why we distinguish between these at all in the data model. They are objectively identical outside of syntax. It is only in the MF2 syntax that these are different.

In a potential combined data model for these two it would also trivial to tell whether a given declaration came from an input or a local declaration, due to the syntax restriction from earlier - if a declaration has the same variable on the left and the right, then it came from a .input, otherwise it came from a .local.

aphillips commented 4 months ago

This representation is not permitted because it breaks immutability. Variable values are immutable in MF2. A value can be annotated using a declaration, but the value itself cannot be changed.

A .input provides a way to annotate (but not change) a value passed in. So .input {$count :integer} says that there must be a variable count passed in (or you'd get a resolution error) and adds an annotation asking for it to be formatted/selected as an integer.

A .local provides a way to assign a new value. So .local $count = {:expression} provides a way of assigning a value to the name count. If you want to change the value of an existing value count, you have to assign it to another name, e.g. .local $count1 = {$count :something}.

This is described by the design on variable mutability

lucacasonato commented 4 months ago

Ok, then my understanding of what .input is is wrong - I described my initial understanding in https://github.com/unicode-org/message-format-wg/issues/818.

lucacasonato commented 4 months ago

This example being allowed makes no sense to me then: https://messageformat.dev/playground/#LmlucHV0IHskY291bnQgOm51bWJlciBzdHlsZT1wZXJjZW50fQp7e3skbmFtZX19fQ.ewogICJuYW1lIjogMTIzCn0.ZW4tVVM

aphillips commented 4 months ago

This example being allowed makes no sense to me then: https://messageformat.dev/playground/#LmlucHV0IHskY291bnQgOm51bWJlciBzdHlsZT1wZXJjZW50fQp7e3skbmFtZX19fQ.ewogICJuYW1lIjogMTIzCn0.ZW4tVVM

The example message is:

.input {$count :number style=percent}
{{{$name}}}

The inputs supplied include name but not count. Since the pattern does not reference $count, the message can format without triggering an error ($count is never used).

An implementation is permitted to do eager evaluation, in which case this message could cause an Unresolved Variable error. However, we have some group members who are strongly committed to lazy evaluation. Such an implementation would never detect the unresolved variable (unless we require every declaration to be evaluated, which breaks lazy evaluation).

Note that lazy or semi-lazy evaluation allows for messages to reference variables that are only sometimes passed in and to annotate those values. For example:

.input {$delivery :datetime dateStyle=long timeStyle=long}
.match {$packagesDelivered :integer}
0 {{No packages were delivered}}
one {{{$packagesDelivered} package was delivered: {$delivery}}}
* {{{$packagesDelivered} packages were delivered: {$delivery}}}

$delivery only matters if packagesDelivered != 0.

Admittedly this sort of message is rare and somewhat unusual.

lucacasonato commented 4 months ago

Whoops - its late here. The right message and data would be: https://messageformat.dev/playground/#LmlucHV0IHskY291bnQgOm51bWJlciBzdHlsZT1wZXJjZW50fQp7e3skY291bnR9fX0.ewogICJjb3VudCI6IDEyMwp9.ZW4tVVM

.input {$count :number style=percent}
{{{$count}}}

This behaves like I had initially thought. I then do not understand why this is not identical to a theoretical:

.local $count = {$count :number style=percent}
{{{$count}}}``

At least in the data model, these are semantically equivalent.

aphillips commented 4 months ago

Those would be semantically equivalent. We don't allow that because we have a consensus around variable immutability. See the design doc quoted elsewhere. But the data model with the change proposed in #799 would no longer care about that (there would be no data model difference). I assume you agree with the change in #799?

lucacasonato commented 4 months ago

Yeah - that change in #799 seems very good.