Skip to content
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 missing "literal" specifier for key equality #996

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

eemeli
Copy link
Collaborator

@eemeli eemeli commented Feb 2, 2025

As identified in #995, the key comparison text should be specified to talk about literal keys, rather than all keys, as that's a bit misleading: Catch-all * keys are never compared to selector values, and they have only one source representation.

@eemeli eemeli added the syntax Issues related with MF Syntax label Feb 2, 2025
@eemeli eemeli requested review from aphillips and mihnita February 2, 2025 13:32
@aphillips
Copy link
Member

I think this is the wrong fix? We don't define literal vs. non-literal keys anywhere to make the distinction here work. Moreover, we need to saying something about the value *.

We have a note (which should probably be normative):

At least one variant is required to have all of its keys consist of the fallback value *. Some selectors might be implemented in a way that the key value * cannot be selected in a valid message. In other cases, this key value might be unreachable only in certain locales. This could result in the need in some locales to create one or more variants that do not make sense grammatically for that language.

To this we need to saying something about how to have a literal that contains or consists solely of *, since we don't provide a backslash escape for it. Something like:

The key value * is reserved as the fallback value. To have a key that consists of *, use the quoted literal |*|.
Note that this is only time a quoted and unquoted literal are treated as distinct.

We should also fix the location in syntax.md:

Implementations MUST NOT distinguish between quoted literals and unquoted literals that have the same sequence of code points.

... probably by adding a note:

Note

The key value * is special. The quoted literal |*| is distinct from the fallback value *, even though the character sequence represented it identical.

@mihnita
Copy link
Collaborator

mihnita commented Feb 2, 2025

I think we should not rush this.

My open issue has two sides:

  • the clarity of the spec
  • the idea that * is the only thing in the spec that has some kind of "type", instead of string

The second one is the part that bothers me and I think is potentially confusing for users without any benefit.

For casual user not reading the spec a lot of things are equal now (user reaction in bracket):

  • foo == |foo| (makes sense)
  • 1 == |1| (why? A number is not equal to a string (in some languages :-)
  • one == |one| (why? one is a standard CLDR keyword for plural. Or we compare an enum to a string)
  • * != |*| (wait, what? why is this single thing different?)

It's easy to explain that "everything is a string, and they compare as strings", and that would clarify the first 3 bullets.
But for some reason the last one is different.

So no, I don't want to rush with a "fix" that enshrines the status quo.
Because I think that the status is not good.

@gibson042
Copy link
Collaborator

So no, I don't want to rush with a "fix" that enshrines the status quo.
Because I think that the status is not good.

Do you have an alternative in mind? I ask because it is necessary to distinguish the catch-all key from all literal values, and the lack of quoting |s around a character that would require them in order to be parsed as a literal is an obvious (probably the most obvious) approach to accomplish that—in fact, it is exactly the same approach that separates function-expression { :foo } from literal-expression { |:foo| }.

@macchiati
Copy link
Member

I see where you are coming from, but it isn't quite right. We provide quoting for literals whose unquoted forms would have different syntactic roles: |$a| is not syntactically the same as $a.

I also disagree with the following statement:

Note that this is only time a quoted and unquoted literal are treated as distinct.

That is incorrect. * is not a literal as a key value, any more than $a is a literal in the following.

{$a :string}

and

{|$a| :string}

A corrected statement would be:

Note that quoted and unquoted literals are never treated as distinct. However, * is not a literal value when it appears as a key; it is a semantic construct.

@macchiati
Copy link
Member

My posted crossed with @gibson042 's.

@aphillips aphillips added the Agenda+ Requested for upcoming teleconference label Feb 3, 2025
@eemeli
Copy link
Collaborator Author

eemeli commented Feb 3, 2025

Replying to @aphillips:

I think this is the wrong fix? We don't define literal vs. non-literal keys anywhere to make the distinction here work. Moreover, we need to saying something about the value *.

The preceding paragraph does define catch-all key, and "literal key" is here used synonymously with something like "key consisting of a literal value". Also the first paragraph of this section concludes with this "A key can be either a literal value or the "catch-all" key *."

We should also fix the location in syntax.md:

Implementations MUST NOT distinguish between quoted literals and unquoted literals that have the same sequence of code points.

... probably by adding a note:

[!NOTE]
The key value * is special. The quoted literal |*| is distinct from the fallback value *, even though the character sequence represented it identical.

This formulation would be a bit misleading, as * does not represent any character sequence; it's syntax for a catch-all key. It is not a literal, is not compared like a literal, and does not have an equivalent quoted-literal representation.


Replying to @mihnita:

the idea that * is the only thing in the spec that has some kind of "type", instead of string

It's not, though. It might be best to compare it to operands and option values, which can be either literals or variable references. Similarly, variant keys can be either literals or catch-all keys.

@mihnita
Copy link
Collaborator

mihnita commented Feb 3, 2025

I thing that a language well designed is something that one can read (and to a degree write) without knowing anything about any spec.

So let's see what we have:

.match $condition

# the same thing
|foo|   {{ ... }}
 foo    {{ ... }}

|many|   {{ ... }}
 many    {{ ... }}

# the same thing
|1|   {{ ... }}
 1    {{ ... }}

# the same thing
|1.0|   {{ ... }}
 1.0    {{ ... }}

# NOT the same thing = not intuitive
1.0   {{ ... }}
1     {{ ... }}

# NOT the same thing.
# Not intuitive because everything above told me that
# the keys are all strings, no matter if they are wrapped in |...| or not.
|*|   {{ ... }}
 *    {{ ... }}

And also give me one other programming language where 1 != 1.0

|$a| is not syntactically the same as $a.
I agree.

And that is also unintuitive, to a degree.
If you would see it as keys in a match (which is not possible),
what would you expect here (without knowing about options, only from the examples above):

|$foo|   {{ ... }}
 $foo    {{ ... }}

The thing that breaks the "intuitiveness" for me I think are the numeric literals.
I think it is that part that "forces" the intuition to expect the keys to compare as strings.


Any attempt to explain the behavior to me by quoting the spec misses the point.
And I am not arguing that the behavior is against the spec.
I argue that the behavior (without reading the spec) is not intuitive.

@gibson042
Copy link
Collaborator

@mihnita I'll ask again, do you have an alternative in mind? If you're arguing to remove unquoted literals you'll have sympathy from me, but unfortunately you won't get any further than that.

@aphillips
Copy link
Member

@mihnita

Note that your examples would actually look like:

.match $something
|foo|   {{ ... }}
 foo    {{ ... }}
*      {{ this is always present }}

Most programming languages have syntactic quirks in which you need to escape certain things in order to use them.

And also give me one other programming language where 1 != 1.0

"1".equals("1.0") == false ?

Richard asked earlier and I'll ask again: what alternative do you propose? We don't have numbers or types and we do have a catch-all key. The string "*" has to be rare as a match value.

@macchiati
Copy link
Member

macchiati commented Feb 3, 2025 via email

@mihnita
Copy link
Collaborator

mihnita commented Feb 4, 2025

And also give me one other programming language where 1 != 1.0

"1".equals("1.0") == false ?

I am not sure if this is a joke :-)

Those are strings, and look like strings.

I would not complain AT ALL if in our case |1| != |1.0|.
They are obviously strings (once you know that | is the string delimiter,
which one can figure out without reading the spec).

But 1 and 1.0 look like "numbers", and we even call then number literals.
In any programming language I know if (1 == 1.0) is true.

Do you have an example where that is not the case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Agenda+ Requested for upcoming teleconference syntax Issues related with MF Syntax
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants