Description
During the last call I gave some feedback that I found it confusing that :function
s 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