Skip to content

Clarify declaration immutability #511

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

Merged
merged 8 commits into from
Nov 6, 2023

Conversation

aphillips
Copy link
Member

Having reviewed @catamorphism's previous text and looking at our current text, these are proposed editorial changes to make variable immutability clearer in the spec. The previous text's "a messages is not considered valid" doesn't read as proper English. We also lack an error for this (we have one for duplicate options), so I added one.

Having reviewed @catamorphism's previous text and looking at our current text, these are proposed **_editorial_** changes to make variable immutability clearer in the spec.
- An _input-declaration_ MUST NOT bind a _variable_ that appears as a _variable_ in a previous
_declaration_.
- A _local-declaration_ MUST NOT bind a _variable_ that appears as a _variable_ in a previous
_declaration_.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought local declarations were allowed to override external variables? Or was that a rejected proposal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little complicated and I should probably add text about the corner case here.

Currently you can annotate an external value using input:

#input {$var :function opt=val}

... and you can assign it to a local variable:

#local $foo = {$var :function opt=val}

... and you can overwrite an external variable so long as it is not declared:

#local $var = {$foo :function opt=val}  // overwrites any `$var` passed in

But not this:

#input {$var :function opt=val}
#local $var = {$foo :something opt=val}

spec/syntax.md Outdated

> [!Note]
> These restrictions only apply to _declarations_.
> A _placeholder_ or _selector_ MAY override the annotation provided in a _declaration_.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's very confusing to call this "overriding", since no new name is being introduced. It's just constructing a new expression.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Try the wording changes in the e9908c8.

spec/syntax.md Outdated
> input {$var :number maxFractionDigits=0}
> match {$var :plural maxFractionDigits=2}
> when 0 {{The selector can re-annotate {$var}}}
> when * {{This pattern can re-annotate {$var :number maxFractionDigits=3}}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exposition is better, but I'm still confused about the semantics. In the scope of the input declaration (the scope where $var is defined), does $var refer to the result of applying the :number formatter to $var with the option maxFractionDigits bound to 0? Or does it just refer to whatever value was passed in for $var? If the latter is true, what's the point of applying the :number annotation in the input declaration? And if the former is true, what does it mean to apply the :number formatter to an already-formatted number (or is :number expected to check whether its argument is a raw number or a formatted number, and handle those cases differently?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our specification is explicit in that it doesn't say whether the declaration is early or late binding, which I think means that it doesn't matter. Another way to say that is that the input declaration means that any reference to $var should be interpreted through the function :number and the passed options.

Maybe the word re-annotate is not right? Perhaps:

Suggested change
> when * {{This pattern can re-annotate {$var :number maxFractionDigits=3}}}
> input {$var :number maxFractionDigits=0}
> match {$var :plural maxFractionDigits=2}
> when 0 {{The selector can apply a different annotation to {$var} for the purposes of selection}}
> when * {{A placeholder in a pattern can apply a different annotation to {$var :number maxFractionDigits=3}}}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if the former is true, what does it mean to apply the :number formatter to an already-formatted number (or is :number expected to check whether its argument is a raw number or a formatted number, and handle those cases differently?)

We don't specify what type exactly the argument to :number should be, but we do say that (a) it should be the resolved value, and that (b) the shape of the resolved value is implementation-specific. If a "resolved value" is allowed by the implementation to be a raw value, then functions must accept such raw values as arguments, in addition to richer "formattable" data structures.

Also, note that the same situation can be reproduced without input, using multiple local declarations:

local $a = {1 :number minFractionDigits=2} // formats as 1.00
local $b = {$a :number minFractionDigits=3} // formats as 1.000

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside: I'm not sure if the spec is currently clear enough about the requirement to allow functions to inspect the raw value and the formatting options of their arguments. Specifically, while a function may eagerly format its operand to a string, it should (must?) also return the raw underlying value and the formatting options used for formatting, in case another function wants to extend them or use them for other logic. Consider:

local $a = {1 :number minIntegerDigits=3} // formats as 001.
local $b = {$a :number minFractionDigits=3} // formats as 001.000
// min integer digits are preserved from the previous call.
input {$item :noun case=accusative count=1}
local $colorMatchingGrammaticalNumberGenderCase = {$color :adjective accord=$item}
// :adjective inspects $item to learn about case=accusative, count=1

Should I file an issue about this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it has to work that way. I don't think "eager" evaluation is necessarily to a string. It is to a "formattable". Otherwise format-to-parts wouldn't work nor would (as you point out) later annotation. Also, selectors wouldn't work:

input {$num :number maxFactionDigits=0}
match {$num}
when one {{ won't get here because {$num} won't be the string 'one' }}
when * {{  :-( }}

What "eager" evaluations might do is copy the value and modify the copy:

input {$startDate :systemTime}
local $endDate = {$startDate :add unit=days increment=6}

spec/syntax.md Outdated
> input {$var :number maxFractionDigits=0}
> match {$var :plural maxFractionDigits=2}
> when 0 {{The selector can re-annotate {$var}}}
> when * {{This pattern can re-annotate {$var :number maxFractionDigits=3}}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if the former is true, what does it mean to apply the :number formatter to an already-formatted number (or is :number expected to check whether its argument is a raw number or a formatted number, and handle those cases differently?)

We don't specify what type exactly the argument to :number should be, but we do say that (a) it should be the resolved value, and that (b) the shape of the resolved value is implementation-specific. If a "resolved value" is allowed by the implementation to be a raw value, then functions must accept such raw values as arguments, in addition to richer "formattable" data structures.

Also, note that the same situation can be reproduced without input, using multiple local declarations:

local $a = {1 :number minFractionDigits=2} // formats as 1.00
local $b = {$a :number minFractionDigits=3} // formats as 1.000

aphillips and others added 2 commits November 6, 2023 07:27
Co-authored-by: Stanisław Małolepszy <sta@malolepszy.org>
Co-authored-by: Stanisław Małolepszy <sta@malolepszy.org>
@aphillips aphillips merged commit b343368 into main Nov 6, 2023
@aphillips aphillips deleted the aphillips-declaration-immutability-improvements branch November 6, 2023 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants