-
-
Notifications
You must be signed in to change notification settings - Fork 34
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
Add a :math function, for addition and subtraction #932
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -381,6 +381,69 @@ together with the resolved options' values. | |
|
||
The _function_ `:integer` performs selection as described in [Number Selection](#number-selection) below. | ||
|
||
### The `:math` function | ||
|
||
The function `:math` is a selector and formatter for matching or formatting | ||
numeric values to which a mathematical operation has been applied. | ||
|
||
> This function is useful for plural selection and formatting of an offset of an input value. | ||
eemeli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
> For example, it can be used in a message such as this: | ||
> ``` | ||
> .input {$like_count :integer} | ||
> .local $others_count = {$like_count :math subtract=1} | ||
> .match $like_count $others_count | ||
> 0 * {{Your post has no likes.}} | ||
> 1 * {{{$name} liked your post.}} | ||
> * 1 {{{$name} and one other person liked your post.}} | ||
> * * {{{$name} and {$others_count} other people liked your post.}} | ||
> ``` | ||
|
||
#### Operands | ||
|
||
The function `:math` requires a [Number Operand](#number-operands) as its _operand_. | ||
|
||
#### Options | ||
|
||
The options on `:math` are exclusive with each other, | ||
and exactly one option is always required. | ||
The options do not have default values. | ||
|
||
The following options and their values are | ||
required in the default registry to be available on the function `:math`: | ||
- `add` | ||
- ([digit size option](#digit-size-options)) | ||
- `subtract` | ||
- ([digit size option](#digit-size-options)) | ||
|
||
If no options or more than one option is set, | ||
or if an _option_ value is not a [digit size option](#digit-size-options), | ||
aphillips marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a _Bad Option_ error is emitted | ||
and a _fallback value_ used as the _resolved value_ of the _expression_. | ||
|
||
#### Resolved Value | ||
aphillips marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The _resolved value_ of an _expression_ with a `:math` _function_ | ||
contains the implementation-defined numeric value | ||
of the _operand_ of the annotated _expression_. | ||
|
||
If the `add` option is set, | ||
the numeric value of the _resolved value_ is formed by incrementing | ||
the numeric value of the _operand_ by the integer value of the digit size option value. | ||
|
||
If the `subtract` option is set, | ||
the numeric value of the _resolved value_ is formed by decrementing | ||
the numeric value of the _operand_ by the integer value of the digit size option value. | ||
|
||
If the _operand_ of the _expression_ is an implementation-defined numeric type, | ||
such as the _resolved value_ of an _expression_ with a `:number` or `:integer` _annotation_, | ||
it can include option values. | ||
These are included in the resolved option values of the _expression_. | ||
The `:math` _options_ are not included in the resolved option values. | ||
|
||
eemeli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#### Selection | ||
|
||
The _function_ `:math` performs selection as described in [Number Selection](#number-selection) below. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this make it clear that the selection is done after the math is performed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could, but it's not technically necessary, as Number Selection includes this:
which ends up referring back to the preceding Resolved Value section, which clarifies when the modifications are done on the resolved value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The following still bothers me:
I think we should replace that by:
I really don't think we want the following two to differ in selection behavior, formatting, etc.
So I'd recommend that the resolved value of :math inherit both the 'datatype' and the options from its operand. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see some significant problems in these formulations.
If that is all the resolved value is, then there is no difference in resolved value between the following two.
But of course there is a big difference: the resolved value of $x1 contains a :integer annotation and the resolved value of $x2 contains a :number annotation, as in:
And it has to be that way, otherwise no .local setting later on could tell the difference between them. So I think L142 of formatting.md is insufficient:
It also needs do have something like: getFunctionAnnotation(); In shorthand, the resolved value needs to logically contain a triple {datatype, functionAnnotation, optionMap}. Otherwise, a subsequent .local statement using a resolved value wouldn't have enough information to know how to compose. So let's look at the following:
But the operand can also be a resolved value, which is neither a literal nor just an implementation datatype; it has additional information. And in multiple cases in this file, we have wording like:
Now, if somewhere we say that the resolved value of any expression with a function contains that function as an annotation, then we could get away with this shorthand (although we also need to fix the wording for options). Otherwise, we need to say:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get it. The composition model is that functions can expose specific options, but the identity of the function itself is not passed. There is no difference between the resolved values of It has to be that way, because otherwise each function would need to understand what all of the other functions (including namespaced ones) "mean". If There are places where I have actively resisted "doing harm" to the resolved value based on the annotation. That's an argument we can have. Mostly I'm concerned that we don't strip information from the resolved value because of some formatting instructions:
We could change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I conflated too many thoughts in my message. So put on hold the notion of the function annotation (not needed for release) and I focus on just 2 issues.
OLD
needs to be changed to: NEW
(with similar changes for other definitions of operand of an X function.)
OLD
changed to something like: NEW
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with the first item is that the resolved value might not be of the correct implementation-defined type. The current text accounts for this by not mentioning. We could say instead:
The second suggestion we already discussed. Saying "map" was thought too prescriptive (ditto "array", "list", etc.). I can see how one might read "options' values" to mean only the values and not the k-v pairing. We might say:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One difference between
The intent with the current language is that a resolved value can be considered as "an implementation defined type". We do not require an implementation to only define a single type that it supports, so both a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From Addison
Wouldn't quite work. Again, we have defined the resolved value of {|3| :number style=percent} to be NOT the same as |3| — since the resolved value includes options. I think what we could say:
I think that is at least headed in the right direction, though not as correct as
That sounds perfect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this formulation is actually counterproductive. The operand is not extracted from the resolved value. The operand includes the accumulated options (which might affect the processing of the function or which might be filtered or ignored). I agree that the operands in the two expressions in your comments have different resolved values, but one of the differences is that In any case, my formulation does not say "from" on purpose. The later "perfect" quote relates to the resolved value of the
Oddly, this discussion appears to be about a stock formulation in all of our functions. Can we merge There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
### Number Operands | ||
|
||
The _operand_ of a number function is either an implementation-defined type or | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
{ | ||
"$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", | ||
"scenario": "Math function", | ||
"description": "The built-in formatter and selector for addition and subtraction.", | ||
"defaultTestProperties": { | ||
"bidiIsolation": "none", | ||
"locale": "en-US" | ||
}, | ||
"tests": [ | ||
{ | ||
"src": "{:math add=13}", | ||
"expErrors": [{ "type": "bad-operand" }] | ||
}, | ||
{ | ||
"src": "{foo :math add=13}", | ||
"expErrors": [{ "type": "bad-operand" }] | ||
}, | ||
{ | ||
"src": "{42 :math}", | ||
"expErrors": [{ "type": "bad-option" }] | ||
}, | ||
{ | ||
"src": "{42 :math add=foo}", | ||
"expErrors": [{ "type": "bad-option" }] | ||
}, | ||
{ | ||
"src": "{42 :math subtract=foo}", | ||
"expErrors": [{ "type": "bad-option" }] | ||
}, | ||
{ | ||
"src": "{42 :math foo=13}", | ||
"expErrors": [{ "type": "bad-option" }] | ||
}, | ||
{ | ||
"src": "{42 :math add=13 subtract=13}", | ||
"expErrors": [{ "type": "bad-option" }] | ||
}, | ||
{ | ||
"src": "{41 :math add=1}", | ||
"exp": "42" | ||
}, | ||
{ | ||
"src": "{52 :math subtract=10}", | ||
"exp": "42" | ||
}, | ||
{ | ||
"src": "{41 :math add=1 foo=13}", | ||
"exp": "42" | ||
}, | ||
{ | ||
"src": ".local $x = {41 :integer signDisplay=always} {{{$x :math add=1}}}", | ||
"exp": "+42" | ||
}, | ||
{ | ||
"src": ".local $x = {52 :number signDisplay=always} {{{$x :math subtract=10}}}", | ||
"exp": "+42" | ||
}, | ||
{ | ||
"src": "{$x :math add=1}", | ||
"params": [{ "name": "x", "value": 41 }], | ||
"exp": "42" | ||
}, | ||
{ | ||
"src": "{$x :math subtract=10}", | ||
"params": [{ "name": "x", "value": 52 }], | ||
"exp": "42" | ||
}, | ||
{ | ||
"src": ".local $x = {1 :math add=1} .match $x 1 {{=1}} 2 {{=2}} * {{other}}", | ||
"exp": "=2" | ||
}, | ||
{ | ||
"src": ".local $x = {10 :integer} .local $y = {$x :math subtract=6} .match $y 10 {{=10}} 4 {{=4}} * {{other}}", | ||
"exp": "=4" | ||
} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a formatting function, is it? A formatting function would require all of
:number
or:integer
's options, e.g. to control zero digits, separators, and the like.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As proposed, you would be able to format a
:math
annotated value, so doesn't that make it a formatting function? If you need the options, you can set them in a separate declaration.