I am looking at all sorts of things for puppet 4 - one of them is how to
exorcise the special cases for puppet's undef and treating it like
nil/null in other languages.
There is really just one special case (as noted earlier in issues) and
that is when assignment of undef takes place to a resource attribute -
in this case users want this to mean; "instead of setting this attribute
to undef, set it to the default value".
When dealing with regular variables this does not matter, we can always
opt to not assign, but there is no way to conditionally perform an
attribute setting (not without making the entire resource expression
conditional - which explodes with permutations if there are several
variables that needs this treatment).
e.g. for regular variables, when assignment of undef is the same as
assigning nil (and not revealing the value from an outer scope) we can
always just skip the assignment. e.g.
In an outer scope:
$x = 'hello'
In an inner scope:
notice $x # notices 'hello'
if $some_value != undef {
$x = $some_value
}
notice $x # notices 'hello' if $some_value was undef
With a resource this is not possible:
define mytype($x = 'hello') { }
mytype { 'title':
x => $some_value # always sets x
}
without doing this:
if $some_value != undef {
mytype { 'title':
x => $some_value # always sets x
}
}
else {
mytype { 'title': }
}
Earlier, I proposed that we should add a new operator 'conditionally
assign attribute' - e.g. something like '?>' which means writing the
expression above like this:
define mytype($x = 'hello') { }
mytype { 'title':
x ?> $some_value
}
i.e., if $some_type is undef, act as if there was no assignment at all
(which will give the default value).
I did not really like this (who needs more operators never before seen
in a language?).
Now however, when implementing a new evaluator there is a different
possibility - we have a perfect candidate for this - the default keyword!
In 3.x. use of default either gets evaluated to the string 'default' or
results in a parse error - which makes it difficult/impossible to use
(except in case and selector expression) as there is no difference
between the user entering the keyword default, and the string 'default'
(or it just did not work), but we can do this differently in the new
evaluator!
Proposal
--------
The keyword undef evaluates to nil, and is a value just like any other.
When boxed to a string it becomes ''.
The keyword default evaluates to a special value (internally the symbol
:default). When boxed to string it becomes 'default'.
The values nil, and :default are passed around as values until they are
needed in boxed form.
The only bit of "magic" (not very magical though) happens in the
evaluation of the attribute => operation, where assignment of :default
acts as if the assignment did not take place at all. (A user that wants
to assign this string value simply uses the string form 'default'
instead of the keyword.
Now the example above can be written like this:
define mytype($x = 'hello') { }
mytype { 'title':
x => $some_value ? { undef => default, default => $some_value }
}
Or using new syntax for if, like this:
define mytype($x = 'hello') { }
mytype { 'title':
x => if $some_value { $some_value } else { default }
}
We could also baked this into a function 'default_if_undefined' to make
it more readable:
define mytype($x = 'hello') { }
mytype { 'title':
x => default_if_undefined($some_value)
}
It is however probably a corner case used by very few and it may not be
worth adding a function for this.
FAQ
* Can I pass default around as a value?
Yes, it dissolves when given as the RHS value to an attribute assignment
(resource expression, resource defaults), but is otherwise a value like
any other.
* Can I append it with +> ?
Yes, that does not dissolve it. It becomes a value in the resulting
array that then can be used in a position where it is dissolved - etc.
* Can I place a default in an array or hash?
Yes, this works
$a = [ 1,2,3, default]
$a = { 'x' => default, 'y' => 10 }
* So it is only when default is the only RHS value it is dissolved?
Yes, if the RHS evaluates to default it dissolves as in these two examples:
mytype { 'title':
x => default
}
$a = [0, 1, default]
mytype { 'title':
x => $a[2]
}
* Can I have a variable named $default ?
Yes, (unless we forbid keywords as variables in puppet 4 - some would
like that restriction).
* Can I interpolate it?
Yes, well, since $default is currently an acceptable variable value we
have to decide what this means:
"This works by ${default}"
But since it is a literal, it is a moot point
"This works by default" # no interpolation needed
If it is a value, it becomes the text 'default'
$foo = default
"This works by $foo"
* How does the keyword default compare?
It is only equal to default. The expression 'default' == default is
false. Likewise in selectors, case, and in expressions.
The operators <, >, <=, >= are undefined - it is neither a magnitude nor
lexical.
Sorry, if this turned into a "mini ARM" :-) I just kept on typing...
Comments?
I am happy with this because:
* $x = undef really sets $x to be undefined, not potentially defined to
something else set in an outer scope. (i.e. it is not anti-matter which
is very hard to grasp).
* It is possible to differentiate between empty string, and no value
* We do not have to translate between :undef and nil everywhere internally
* The use of default as an r-value is new and the only affected logic is
the 3x magic attribute set to default when set to undef.
With this, one part of the complexity moves to the implementor of a
resource-type or a class, since undef can be passed as a value. This is
already required to some degree since there must be checks for empty
string. IMO the way to solve this is to offer better and simpler
parameter assertions - but that is a separate topic.
Regards
- henrik
--
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 post to this group, send email to puppet-dev@googlegroups.com.
Visit this group at http://groups.google.com/group/puppet-dev.
For more options, visit https://groups.google.com/groups/opt_out.