We discussed this proposal in the ad hoc today. We generally agree that going with a prefix is the best choice, and that perhaps "$" would be the best prefix, as it would be least likely to cause conflicts. See
YAML 1.2 and
YAML 1.1, specifically the "Indicators" section in the Index, as well as "Example 5.10" and "Example 5.9" (the grave character was added in YAML 1.2 as reserved for future use). YAML already stole all the best characters, so "$" remains. :)
We discussed two approaches to parsing:
"BROAD" PROPOSAL
Any string value that begins with an unescaped "$" would be considered an "_expression_". This includes map keys, including a-map-with-a-single-string-key-that-has-a-list-as-its-value, which is our conventional function format. But this also leaves the door open to other kinds of special values, such as magic words and other special features.
There's a lot of flexibility in this approach in that it can allow implementations based on anything you can do in YAML, including notations that we do not currently use in TOSCA. A few examples:
# Using our current function signature
my-property: { $get_env: [ USER ] }
# A custom function signature: string argument
my-property: { $get_env: USER }
# A custom function signature: map argument
my-property: { $get_env: { local: USER } }
# A custom string
my-property: $env.USER
# A custom key
my-property:
 key1: value1
 key2: value2
 $sorted: ascending
# A custom list element
my-property:
 - { $sorted: ascending }
It might also be a good idea to disallow node template names from beginning with an unescaped "$". The reason is that a common use case for the "custom string" might be for "modelable entity names" in functions like $get_property and $get_attribute, namely the built-in $self, $source, and $target. This rule would thus avoid potential ambiguities.
The pros of this "broad" approach are:
- Lots of flexibility! Many cool syntax varieties and features are possible.
- Straightforward parsing: any string beginning with an unescaped "$" is an _expression_ (meaning that it will not be treated as a string value).
- This does mean that users will have to pay attention and escape any actual string value beginning with "$". Though note that if they make a mistake they should get a clear error.
The major con of this approach is the lack of a specified function syntax. The fact that TOSCA's built-in functions are all a-map-with-a-single-string-key-that-has-a-list-as-its-value would simply become our convention. However, it's really more than a convention in TOSCA right now, because TOSCA supports nested function calls:
my-property: { $concat: [ { $get_property: [ $self, name ] }, -suffix ] }
If we let "$" be entirely implementation-specific then the TOSCA parser won't know to look into the values (function arguments in this case) and parse them first and somehow embed that _expression_ evaluation.
The workaround would be to say that the TOSCA parser *will* parse "$" values even if they are embedded inside other "$" values. This might sound obvious, but it's non-trivial: it means that custom implementations will not actually have complete control over their values. To me that's a reasonable compromise, even if it limits the flexibility a bit.
"NARROW" PROPOSAL
This is more conservative. The idea is to keep the current TOSCA syntactic conventions for function calls. So, the "$" prefixed strings would only apply to a-map-with-a-single-string-key-that-has-a-list-as-its-value. Additionally, we are assuming that every argument in the list is itself a value, which in turn could also be a "$" function call.
We can furthermore specify that an unknown function name is *not* an error. This will ensure better portability than the "broad" proposal. A TOSCA parser encountering an unrecognized function can just assume that it returns a value of the correct type. It can go further and insert a function call stub (this is what Puccini does), which could then be implemented by the runtime environment itself.
There is additionally another variant of this proposal that would allow for "special words". This is just syntactic sugar for function calls with no arguments, the ability to make these two equivalent:
my-property: { $get_property: [ { $host: [] }, name ] }
my-property: { $get_property: [ $host, name ] }
Note that there's a risk with this variant of turning it into the "broad" proposal above, because this means that any bare string value (but not a map key) needs to be checked for the "$" prefix. The workaround is to restrict this syntactic sugar to be *only* for function arguments. So the following would *not* be a function call, because it's not a function argument:
my-property: $host
Also, again node template names should be disallowed from beginning with an unescaped "$".
Pros:
- Keep TOSCA from becoming too weird.
- Better portability, as noted above.
- Clearer error messages.
Cons:
- Not very straightforward parsing. The "broad" approach is straightforward.
- Too narrow. Why not allow the freedom that the "broad" approach allows?
"COMBINED" PROPOSAL
Bear with me here, this might sound complex but it really isn't very. :)
The idea is to allow the use of "$" everywhere, just like the "broad" proposal. However, if the "$" is structured as a-map-with-a-single-string-key-that-has-a-list-as-its-value then will consider it specifically to be a function call, meaning that again unrecognized function names do not have to emit and error and can be implemented as stubs.
This does mix some of the pros and cons of both approaches.
MY THOUGHTS
Implementations will do whatever they want anyway. However, I do think the idea of recognizing function calls, specifically, is an important and portable feature. Thus I am leaning on the "combined" proposal.