I'm taking this discussion to the list, because it's much better for  
discussion.  Here's the original text from the ticket, with my  
comments interspersed as necessary:

 > I am a newcomer to puppet. This is bad as I don't know all the nuts  
and bolts of the framework. But it is good as well as I still have a  
"fresh view" of puppet as a tool. So please forgive me if I am telling  
nonsense ... Anyhow I'd like to share some "fresh-view" conceptual  
thoughts with you.
 >
 > I am really having conceptual (and practical!!) trouble with the  
semantics of puppet's "classes" and "definitions" as they are defined  
today. IMO the distinction is quite arbitrary and counter-intuitive.

Just so you know, this has come up multiple times, and I remain  
convinced they have separate purposes.

Also, to be clear, I *wholly* encourage this kind of discussion and  
recommendation.  I've never claimed I have the ultimate vision of  
Puppet's language, and I'm always looking for recommendations to make  
it better.  Of course, I will always defend the existing syntax, and I  
will always fight to keep the basic vision I have intact.

The goal of a definition is to function as a composite resource,  
allowing multiple Puppet resources to look like a single resource.   
This makes it easy to do things like have an exec look like an  
iptables rule, for instance.  The key to definitions is that they  
allow you to create new resource types that can be used with the exact  
same syntax as builtin resources.

The point behind classes, however, is to delineate all of the  
resources necessary to provide a given service.  I often call them  
'service classes', rather than just 'classes'.

Given this, there are really two questions:

1) Are these actually two separate purposes?
2) Have I chosen syntax that does a good job of meeting each purpose?

I maintain these are two clearly separate purposes.

However, I tend to think that the syntaxes aren't necessarily that  
great, and I definitely agree that we need more functionality for  
configurating classes while at the same time the functionality we have  
is a bit not-so-great.

 >  AFAICS there are three main distinctions between classes and  
definitions:
 >
 > 1) classes are singletons from a node perspective, definitions are  
instantiable
 > 2) definitions receive parameters, classes do not
 > 3) classes can declare "class variables" ($class::var), definitions  
cannot

These are more syntactical and structural distinctions, but to me, the  
distinctions that matter are how they are and should be used.

 > I have seen many examples in "recipes" where classes were used like  
this:
 >
 > node abc {
 > $globalvar1 = ...
 > $globalvar2 = ... }
 >
 > include xyz
 >
 > class xyz {
 > ...template("consumes <%= globalvar1 %>...")... }
 >
 > file { ...owner = $globalvar2... }
 >
 > IMO this is counter to basic principles of information hiding and  
modularization. (The most exhaustive discussion of the disadvantages  
of such an approach is probably in Steve McConnell's "Code Complete").  
If you do not have the most simple classes in your manifests then  
puppet invites you to use global variables. I can already see from the  
few things I did that this quickly becomes a maintenance nightmare and  
makes puppet classes very difficult to use in a more complex setting.

I agree, and I've been trying to find a better syntax for this.  I  
don't think the answer is to just use definitions, though.

 > Definitions accept construction parameters and are therefore able  
to hide their inner workings and complexity from outer context. This  
makes them more flexible and attractive for modularization than  
classes. Definitions are however not meant for usage as node  
singletons. IMO it's always bad if you try to coerce a syntactical  
construct into a role that it wasn't meant for. Defintions also have  
another big disadvantage: They do not allow for instance variables  
like classes do (something like $mydefinition["xyz"]::myvar).
 >
 > Furtheron the distinction between "classes as singletons" and  
"definitions as prototypes" seems quite arbitrary to me. What is  
called a "class" in puppet is conceptually a class with "automatic  
instantiation" (with one "class instance" per node). This is confusing  
to me as an OO programmers who considers classes and instances  
separate conceptual entities. On the other hand what is called a  
"definition" in puppet is conceptually really a class as it defines a  
"blueprint" of an object that can be instantiated separately. The  
conceptual inconsistency reveals itself in the Class["classname"]  
syntax which is a workaround for the fact that puppet classes are  
instances of classes as well. Definitions on the other hand can be  
referenced quite intuitively with the Definition_name["instance id"]  
syntax.

Part of the problem is that Puppet's language isn't a real  
"programming" language, but I (possibly foolishly) chose many terms  
that mean somewhat different things in other languages.  Part of the  
reason I did this is I was reusing language in tools like cfengine.

But the point remains:   A Puppet class is more of a service class  
than something like an OO class; it can also be thought of as the code  
that specifies a class of machines -- that is, if you've got a  
sendmail-class server, then you get sendmail-related resources.

 > IMO the conceptual distinction between classes and definitions is  
confusing and unnecessary at the same time. To me it would be much  
simpler if such a distinction did not exist. Rather than two resource  
containers you could simply have one generic class container without  
loosing any functionality and winning flexibility, intuitivity and  
maintainability:
 >
 > Here my propositions:
 > 1) Classes should be reframed as universal "blueprints" of resource  
containers that can (and must) be instantiated (like definitions today).

How would you instantiate a class?

 > 2) All classes may define constructor parameters (like definitions  
today).

This seems reasonable and desirable.

 > 3) Classes may be defined as node level "singletons" or node level  
"prototypes". The distinction between singleton and prototype is well  
known to most OO programmers.

How would this distinction be made?

 > 4) Classes can define public instance variables that may be  
accessed from an outer context (like they do today). The access syntax  
would be ($Classname["instance id"]::myvar).

Ugh.  Not so fond of this.  Really, I'd like to get rid of the  
existing syntax, I just knew that something like it was necessary  
until we had a better way of providing good encapsulation and  
configuration for classes.

And what about singleton classes?  Would you support classes without  
instance names, and if so, what would that syntax look like?

 > 5) Instances of classes can be referenced like definitions today:  
Classname["instance id"]

This is just an automatic result of other bits above.

 > All this can be introduced in a fully backwards compatible way  
(which is certainly a must-have!):
 > 1) Definitions will continue to exist but will be deprecated.

Don't definitions provide most of the functionality you want in your  
classes?  Wouldn't it make more sense to use them as the new 'class'  
construct?

 > 2) Classes will be node singletons by default.
 > 3) Class constructor parameter declaration is optional.
 > 4) Singleton classes can still be referenced as Class["xyz"]. This  
usage will however be deprecated.
 > 5) Class variables can still be referenced as $classname::var. This  
usage will however be deprecated.

What about singleton classes classes that don't have multiple instances?

 > 6) You can define "inner classes" to replace today's construct of  
definitions within class context.

So today's definitions could only ever be defined inside other classes?

 > Here is how I'd define the syntax of the ideal resource container  
(replacing both classes and definitions), not really BNF but  
somehow ;-) :
 >
 > {singleton|prototype} class <classname> [($par1, [$par2, [...]])] {
 > ...
 > $instance-var = ...
 > ... {singleton|prototype} class <inner class> [($par1, [$par2,  
[...]])] { ... }
 > }

I can't figure out what you mean here.  Can you show me some example  
classes?

 > The whole thing certainly is not very well thought out yet. But I'd  
love to see a discussion starting from here as I am really fighting  
with the current syntax...
 >


-- 
Wear the old coat and buy the new book. -- Austin Phelps
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to