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.

Reply via email to