Thank you everyone that commented on the first RFC for Resource Defaults
(and Collection) - some very good ideas came up. I am creating a new
thread because I wanted to take the Collection part in a separate
discussion (honestly I would like to burn the entire implementation -
but that is separate topic), and I want to present some new ideas about
defaults triggered by David Schmitts idea about defaults applicable to
only a resource body.
Let me back up and first describe a problem we have in the grammar.
The egrammar (as well as the current grammar in 3x) tries to be helpful
by recognizing certain combinations as illegal (an override that is
virtual or exported), a resource default or override cannot specify a
title. This unfortunately means that the grammar has to recognize
sequences of tokens that makes this grammar ambiguous and it has to be
solved via operator precedence tricks (that makes the problem show up as
other corner cases of the grammar). (This is a classic mistake of trying
to implement too much semantics in the grammar / parser).
So...
What if we simply made the three resource expressions (create resource,
set resource defaults, an resource override) have exactly the same
grammar, and push of validation to the static validation that takes
place and the runtime.
Basically the grammar would be (I am cheating just a little here to
avoid irrelevant details):
ResourceExpression
: At? left_expr = Expression '{' ResourceBodies ';'? '}'
;
ResourceBodies
: ResourceBody (';' ResourceBody)*
;
ResourceBody
: title = Expression ':' AttributeOperations ','?
;
AttributeOperations
: AttributeOperation (',' AttributeOperation)*
;
AttributeOperation
: AttributeName ('=>' | '+>') Expression
AttributeName
: NAME | KeywordsAcceptableAsAttributeName
;
# Details here irrelevant, meaning is: virtual or exported resource
# AT is the '@' token
At
: AT
| AT AT
| ATAT
;
So, how are the three kinds expressed? Notice that a title is required
for each ResourceBody. So we are basically going to handle different
combinations of left_expr and titles. We simply evaluate the left_expr
at runtime and treat the combinations of the *resulting* type and type
of title:
If left (result) is a String, it must be the name of a Resource Type.
The title works as it does now in 3x. In addition the title (of one
resource body) may be Default, which sets the defaults for this resource
expression only (all bodies in the same resource expression) - i.e.
"Schmitt style".
notify { hi: message => 'hello there' }
file {
default:
mode => '0331',
owner => 'weird';
'/tmp:foo':
content => 'content 1'
}
If the left is the keyword 'class', it works the same way as for when
the left is a String but defaults can now only set defaults for meta
parameters since there are no other attributes that work safely across
all classes. (Yes you can do this today with Class { } in 3x)
class { 'a::b': param => value }
class { default: audit => true }
If the left is Type[CatalogEntry] (i.e. a resource or class reference),
the meaning changes to either a default or an override. An example is
probably easier that lots of words to describe this:
Define the defaults for all instances of the resource type Notify:
Notify { default:
message => 'the default message'
}
Override the message for the notify resource hi.
Notify { hi:
message => 'ciao'
}
If the left type is instance specific it is now an error (since a title
followed by ':' is required to create a less ambiguous grammar) - if we
allowed this, a user could write:
Notify[hi] { bye: message => 'adieu' }
we allow a very odd statement (and the reason why resource overrides
currently does not allow a title in its "resource body"). So, an error
for this case.
Since what I propose simply evaluates the left expression there is no
reason to deny certain expression in this place, and it is possible to
use say a variable as indirection to the actual type.
$a = Notify
$a { hi: message => 'hello there' }
(Which is *very* useful to alias types). Strings can also be used - e.g.
'notify' { hi: message => 'hello there'}
which also makes the grammar more symmetric (a bare word like notify is
just a string value i.e. 'notify'). (We still would not allow types
to have funny characters, spaces etc. but it is at least symmetrical).
The current combination of (peculiar) grammar and static analysis of the
left expression to determine which "shape" the resource expression has
means that the parser cannot differentiate between a reference to a
type, and a reference to a specific instance (in general) since all it
sees is
Capitalized { ... }
Captialized[ ... ] { ... }
And thinks the first is a default, and the second an override. In the
new type system however, Resource['notify'] is the same as just a
Notify, and thus, these longer reference simply does not parse (it looks
like Capitalized[...] to the parser / static "shape" detector.
(This could be fixed the ugly way of making the "shape detector" aware
of the built in type names - but that is... just ugly.
I think it is possible to do this in a way that makes it possible to
deprecate the 3x style in 3.7 and change it in 4.0. There is breakage
here for sure, but we are already breaking the scoping of the defaults
(and it was even suggested to remove them completely as they are
typically used as syntactic sugar (and we are removing the more
"powerful" (and confusing) ways defaults works.
Anyway, a few corner cases exist. A +> is only allowed if it is a
default or override expression. This means that we can only statically
check (or rather not check) a default resource body for use of +> (they
are allowed). The other forms depends on the type of the result of
evaluating the left_expr, and thus that check must be done at runtime
(unless the expression is a Literal - which is what is in all existing
3x code).
Summary:
By treating all three kinds of resource expressions the same way
grammatically we get a cleaner grammar with fewer corner cases (less
strange errors). The use of 'default' becomes symmetric; when applied to
a type, it sets the defaults for that type, and when applied to a
resource body (bodies) it applies to those bodies. Overriding something
requires the title, it just moves from the left side to be a title like
in the original, only that the left is now an upper cased type reference
(e.g. notify {hi: } = regular, Notify { hi: } = override.
As the implementation of this requires a number of iterations of grammar
changes I have not tried out what I propose in practice (but
I think it should work as it makes the grammar much simpler).
Do you like these ideas? Is it worth trying to make this work to try it
in practice? (it will take 1-2 days for the implementation, and a bit
more to fix all breaking tests).
My own main issue with the idea is that it makes code backwards
incompatible; you cannot write a manifest that uses defaults and
overrides in a way that works both in 3.x and 4.x. (Or, I have not
figured out a way yet at least).
(This is a cleanup that I have wanted to do for quite some time, and
David Schmitt's proposal triggered all of this - YES, we do have a
'default' literal that we can use !)
Looking forward to comments, and wb all 4th July celebrating peeps.
And finally, an alternative regarding Overrides, if we want to keep the
left side to be resource instance specific, (i.e no title), we could
simply change it to an assignment of a hash. I.e. instead of
Notify[hi] { message => 'overridden message' }
you write:
Notify[hi] = { message => 'overriden message' }
And now, the right hand side is simply a hash. The evaluator gets a
specific reference to a resource instance, and knows what to do.
(We could also allow both; the type + title in body way, and the
assignment way).
Now I have typed too much already...
Regards
- henrik
--
Visit my Blog "Puppet on the Edge"
http://puppet-on-the-edge.blogspot.se/
--
You received this message because you are subscribed to the Google Groups "Puppet
Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to puppet-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/puppet-dev/lpcsvg%2415g%241%40ger.gmane.org.
For more options, visit https://groups.google.com/d/optout.