Hi Luke,

thanks for your reply. I really appreciate that you took the time to
respond... and IMO you brought forward some very good arguments! :-)

> Also, to be clear, I *wholly* encourage this kind of discussion and
> recommendation.

Nice! That makes a big difference.

> Of course, I will always defend the existing syntax, and I
> will always fight to keep the basic vision I have intact.

You'd be a bad project leader if you didn't.

> The goal of a definition is to function as a composite resource,
> allowing multiple Puppet resources to look like a single resource.
> 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'.

I think this distinction inherited a lot of cfengine and comes natural
to someone who worked with cfengine for years. I was aware of
cfengine's semantics and grammar when I made my post. I however
believe that cfengine did very much "organically grow" over the years
rather than being "consistent" or "well thought out". Puppet already
does a *good* job in addressing some inconsistencies in cfengine
though!

> 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.

If classes gained the ability to declare "instantiation parameters" so
that encapsulation and information hiding can be enforced with classes
then my main objection to classes would vanish and I as a user would
be completely happy with that, even if you maintained the semantical
distinction between classes and definitions.

Btw: Now that I worked a little more with puppet I see the same
problem with templates. They don't declare the parameters they use.
IMO that's a not-so-good idea. But this is somehow less important in
practice as templates are a lot more "local" than classes are.

I also agree with you that public class variables (or potentially
public attributes of definitions) are not really necessary although I
do not consider them "evil". IMO they serve the purpose of providing
self-documenting parameters to other services (e.g. being able to use
ssh::port in a firewall rule is nice) without breaking encapsulation.
They also help to explicitly identify and declare dependencies between
services (i.e. "automatic require relationships"). And as "definition
attributes" they would provide a nice way of accessing internal read-
only parameters (e.g. "package::platform") which is not possible today
AFAIK. If public class/definition attributes were missing, however, I
could well do without them.

You also said that deprecating syntax constructs is not "backwards
compatibility" and you are right with that! I admit that my "backwards
compatibility" was rather superficial or simply wrong. Adding
"instantiation parameters" to classes and public attributes to
definitions would however be fully backwards compatible I think. I
withdraw the rest of my proposal.

So overall that's already it. If you are short of time then you can
stop reading here and you got all the most important. I think we now
mainly agree on which improvements could be made if someone found the
time and priority to do it.

**************************************************

If you're interested in a more "abstract" discussion of my "ideal"
definition language for a configuration tool, then you may read on. I
think I gave answers to all of your more specific questions here.

You have been warned though! The remainder is more of an article than
of a post. And I won't consider backwards compatibility from now on as
the above practical solution does a good job on that I think.

Anyway: I think that being fully aware of what "would be nice" if
there wasn't backwards compatibility is wonderful for you as a
language designer because it gives you direction in implementation
details even if you cannot implement the "pure" solution for practical
reasons. It also helps users like myself to program into the language
rather than programming in the language (see the book "Code Complete"
again for that distinction) thereby avoiding maintenance problems.
Both are real practical and money-worthy advantages although based on
"conceptual" rather than "implementation" knowledge. I am a
practitioner and not a scientist!!

> Part of the problem is that Puppet's language isn't a real
> "programming" language

I don't agree on that. It might be a specially declarative and less
procedural/imperative language and is probably not turing complete (no
idea about that, I didn't try the proof!) But maybe this is just an
argument about wording and therefore not so important.

> 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.

I agree with you that it should be the purpose of puppet's language to
describe bundles of configured resources. But that's already it, no
further distinction necessary.

The rest is cfengine heritage which I do not consider valuable when
defining the "ideal" language based on design best-practices. It
certainly is very valuable for cfengine adepts who "upgrade" to
puppet. But that's not the point here (see the practical solution
above for cfengine-think compatible changes).

What you probably want to say is that cfengine or puppet classes are
somehow cross-cutting aspects of nodes (in AOP words) while
definitions are not. But what's the real purpose of AOP? It's just
introducing or modifying procedural methods in bulk to avoid code
duplication. This does not make sense in a language without a
procedural element! My hypothesis is: Any non-procedural language can
be considered class- and aspect-oriented at the same time. If I
introduce a puppet definition into a puppet class then you introduce
some powerful "aspect" into this class using a very concise language
without any code duplication. You could always factor out repetitive
code into puppet classes /or/ definitions without any problems even
when this code will be used in completely different contexts later. So
no required distinction here either.

If you think well then an OO language is nothing other than a hybrid
declarative/imperative language. If you leave out the imperative part
(i.e. methods!) then you have a very nice framework for a fully
declarative language which is very similar to puppet's language. If
you'd even disallow public attributes then you'd get something very
similar to one of those many functional languages "à la
mode" (Haskell, Erlang, ...) these days.

To fully "disillusion" the cfengine heritage: To me a "node" is a
resource as well, I don't see any required semantic difference here to
other resources like a web server software or a firewall. I could also
imagine having higher level clusters of nodes being treated as a
"resource bundle". Why not define a class of nodes with instantiation
parameters and thereby handle a cluster of machines as "one thing"? If
we had a construct like that I could probably eliminate another bunch
of global variables and redundancy in my puppet scripts.

IMO it is completely irrelevant whether you add a resource (node) to a
class (in cfengine parlor) or whether a class bundles resources as
puppet's definitions do. If you agree that a node is a resource and
that aspects and classes are essentially the same in a declarative
language then what's the difference??? You end up with classified (or
bundled) resources, don't you? Just a chicken or egg problem IMO.

The same applies to puppet types. They just abstract away some OS
level resources. To me types are not necessarily semantically
different from classes, nodes or definitions.

So even the node and type constructs become unnecessary if you think
like that. You could express everything you have today in puppet with
resource bundles that take instantiation parameters, allow inheritance
and maybe define public attributes. I bet I could reformulate
everything I can do in puppet today with a simplistic language like
that.

Here another "disillusionment", this time concerning singletons and
prototypes: A singleton is always only a singleton within a certain
context. So in Java or C++ you might do some tricks to define a
singleton. But then you end up with a singleton in the context of a
class loader (Java) or process (C++)! As soon as you work with
application server clusters (Java) or several processes in parallel (C+
+), you have to synchronize all those "singletons" or invent
singletons on cluster level (what some application servers and
parallel processing frameworks effectively do). Think cloud computing
and you're at the next level of abstraction, and so on...

The exact same applies to puppet singletons. Puppet classes may be
singletons on a single node but yes they are prototypes if you
consider a cluster of nodes as an entity in its own right. The fact
that you can safely and implicitly consider classes singletons in
puppet is because you are running puppetd on a node. But what if you
wanted to cluster nodes one day as one "thing" to avoid the current
duplication of node-specific code in puppet? Oups! You'd have to
introduce ... named puppet class instances.

IMO a resource can only be a singleton in the context of a bundle. Not
more not less.

So once we see that we really only have to describe bundled resources
then we can do with one single syntactical construct. Let's call it a
"bundle" for now to avoid all mis-interpretations in the sense of OO
concepts or functional languages or cfengine heritage.

Here a translation table for cfengine and puppet semantics into
"bundle semantics":

cfengine input files (bundle) group cfengine action instances
(resources)
cfengine classes (bundle) group hosts (resources)
puppet nodes (bundle) group puppet classes and definitions (resources)
puppet classes (bundle) group puppet types, classes and definitions
(resources)
puppet definitions (bundle) group puppet types, classes and
definitions (resources)
puppet types (bundle) group OS configuration files, services,
packages, etc. (resources)

Imagine what such a simplification would mean for documentation and
parser implementation. You'd probably be able to through away large
junks of specialised node, class, definition and type code/
documentation. And doing the same thing with less code is always a
good thing. (Wasn't it Mark Twain who apologized that he couldn't
write a shorter text because he didn't have the time to do so? I
apologize too...)

But yes: You cannot do this in a backward compatible way.

Based on this long "foreword" I can quickly answer your remaining
questions:

> How would you instantiate a class?
> And what about singleton classes?  Would you support classes without
> instance names, and if so, what would that syntax look like?
> How would [the] distinction [between singletons and prototypes] be made?

If we re-interprete a class as a bundle then we get:

bundle-name { par1 => ..., par2 => ..., ... }

This looks exactly as an instance of today's definition without an
instance name. The instance name is however implicitly defined and
defaults to the containing bundle's name.

For a class in the context of a "node bundle" this would automatically
be the node name. If you nest classes then the node name would
automatically propagate to lower level classes. So this is just a
special case within the general bundle framework now. A very elegant
solution IMO.

Including classes within definitions is possible today. I don't
understand what this really means in your own definition of classes as
node services and definitions as resource bundles. So I won't use it
now. Anyway there is an easy workaround for this in our "bundle
framework" by including the class "bundle" at a higher level. This is
semantically identical and IMO cleaner anyway as it enforces real
encapsulation and disallows code duplication. As we are not concerned
with backwards compatibility here this is ok.

More generally: Any instantiation without name would be considered a
"singleton" instantiation in the context of its bundle. The instance
name would always default to the bundle's instance name. If you try to
instantiate two "name-less" bundles within the same context you'd get
an error. Like that we could even do away with the "singleton/
prototype" keyword that I proposed in my initial post. And you'd get a
very nice upgrade path if you discover all of a sudden that you want
two ssh services running in parallel on one node. Simply transform
your ssh singleton into an ssh prototype by giving it an explicit
name.

> 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?

Yes absolutely! If you add public parameters to definitions, they are
exactly my intended target construct. I only retained the "class" word
rather than the "definition" word as it is closer to what is commonly
seen as a class in OO languages and rings the right bell in the head
of somebody trained in OO (of which you probably have as many if not
more as former cfengine users). To keep "the right part" of your
cfengine thinking you might also call it an "aspect" following my
hypothesis of class/aspect equality in declarative languages above .

>  > 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?

No. Definitions or "bundles" as I like to call them now, can live
within the top level context. I think that this has become obvious
from the previous discussion.

Hope that I could answer all your questions. I hope that this was at
least an interesting read though maybe not what can be actually
implemented in the foreseeable future.

Florian

--~--~---------~--~----~------------~-------~--~----~
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