Skip to content

[FEEDBACK] The re-use of :function between "annotation" and "output" positions can be a bit confusing #818

Closed
@lucacasonato

Description

@lucacasonato

During the last call I gave some feedback that I found it confusing that :functions are used in two (or maybe three) somewhat distinct ways, without any obvious syntax difference. @stasm asked me to open an issue.

Render functions / formatting functions

Inside of a placeholder expression in a simple message or quoted pattern, and also in the expression in a local declaration, the function acts as a rendering function, that determines how a value is formatted into the output. In code, this would be equivalent to:

formatted_value = f(value)

As far as I understand, the formatted_value could be a string, but it could also a more complex value that the renderer then will render in some way - like calling toString() on it in JS for example.

So really it'd be more something like

formatted_value = f(value)

formatted_value.toString()

I think this mostly makes sense and is relatively clear. (I do find it confusing that this essentially allows you to use annotation expressions in a way that is more or less identical to markup, by having the renderer special case some shapes of formatted_value, but I digress.)

Match selectors

Match selectors also look like placeholder expressions. Here however my first intuition tells me that the function is effectively being called with two arguments: the value, and an expected value (the key in the variant). It also does not return a string or more complex value for the renderer, but it returns a boolean.

matched = f(value, key)

After thinking about it some more, I think what it is really trying to model is:

// selector
formatted_value = f(value)

// variant
matched = formatted_value.match(key)

I think that makes much more sense - but this is not explained at all anywhere. If this is the mental model that we think developers should have, we should try to make that clearer.

This mental model is however undermined by the fact that annotations are required in expressions in selectors. If they were not (which I am not advocating for, because it would probably lead to worse tooling), the second mental model would work quite well - it could be simply explained.

What is also very unclear to me, is what it means to have formatting options on functions annotations in expressions in selectors. What does it mean to match on a :datetime style=short, are we matching on the underlying string value, or on the object representing date-time?

Input declarations

Input declarations also use the same placeholder expression syntax. Here however, the syntax initially looks like :function is a pure type annotation on the input variable. So in pseudocode:

fn message(value: function) {
  ...
}

This however is not at all how this behaves. The input declaration behaves much more like a local declaration that assigns the same variable as it operates on:

value = f(value)

I think that expressing this as .local $value = {$value :function} is actually much clearer. This is not valid syntax right now however.

EDIT: @aphillips corrected me - my understanding here was wrong. Indeed this does really behave just like a type annotation, which I find makes it even more confusing. For example, this is allowed right now:

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

But it does not actually format anything in percent style.

I would find the syntax less confusing if the behaviour were identical to this imaginary local declaration:

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

Alternatively, a different syntax for type annotations would also work to disambiguate this from the other annotation expression things.

EDIT 2: I had misunderstood. My original understanding was correct

Metadata

Metadata

Assignees

No one assigned

    Labels

    Preview-FeedbackFeedback gathered during the technical previewresolve-candidateThis issue appears to have been answered or resolved, and may be closed soon.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions