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.

Reply via email to