The Parameters supported in different kinds of definitions share a common
grammar for parameter definitions - the ParameterList
rule:
ParameterList
: '(' parameters += Parameter (',' parameters += Parameter)* ','? ')'
;
Parameter
: type = Expression<Type>? captures_rest='*'? name = QualifiedName
('=' default_expression = Expression)?
;
Some features may not be semantically valid for a particular kind of definition. Classes and Defines does not currently support "captures_rest". This may change in the future.
When arguments are given during evaluation (i.e a function call, declaration of a class or resource) the argument values are assigned to variables as specified by the definition's parameters.
If a parameter is not given a value:
- An automatic data binding lookup is performed.
- If that did not produce a value, a specified
default_value
expression is evaluated and its result is used as the value of the corresponding variable.
In all cases:
- If the parameter's type does not accept the value (a given argument, value from data lookup, or value from default expression evaluation), an error is raised.
When a default value expression is evaluated, the runtime uses a special parameter scope. This is an intermediate scope that is used only while assigning argument values, performing lookups and evaluating default value expressions. Once all parameter variables have been given their value, this context ceases to exist, and the resulting variables are set in the scope that is used to evaluate the body of the definition (e.g. the body of a function).
The parameter variables are give values from left to right. The parameter's value is bound to a variable that is available in the Parameter Scope. This means that default value expressions can access variables that are to the left (comes before) the parameter being processed.
The Parameter Scope, in addition to the parameter variables, also provides access to the top level scope.
Access to Parameter Variables, and Variables in 'scope': |
---|
Since Puppet 4.4.0 |
Are defined by this specification. A strict, "variable to the left may be referenced" rule applies. The rules for how to access variables in other scopes are also specified. |
Before Puppet 4.4.0 |
The behavior was undefined. In some cases variable values to the left could be accessed,
in other cases it did not work. Default value expressions could in some cases
reference variables in the calling scope! It could also introduce variables
in the calling scope as a result of the undefined and untested behavior.
For versions before Puppet 4.4.0, default value expressions should only use literal values or make references to fully qualified variables that a user knows are already evaluated. |
Function |
---|
function example($a = 10, $b = $a) { } |
When called like this... | the parameters are set like this: |
---|---|
example(0) |
$a = 10 $b = 10 |
example(2) |
$a = 2 $b = 2 |
example(2, 5) |
$a = 2 $b = 5 |
Function |
---|
function example($a = 10, $b = $c, $c = 20) { } |
When called like this... | the parameters are set like this: |
---|---|
example(1,2,3) |
$a = 1 $b = 2 $c = 3 |
example(1,2) |
$a = 1 $b = 2 $c = 20 |
example(1) |
error, default expression for $b tries to illegally access not yet evaluated $c |
Numerical variables refer to the result of the last performed regular expression match. When evaluating parameters, these are reset before evaluation of each parameters' default expression. numerical variables are undef when there is no preceding match, or where such a match did not produce a matching capture for the numeral.
Function |
---|
function example($a = $0, $b = $1) { } |
When called like this... | the parameters are set like this: |
---|---|
example() |
$a = undef $b = undef |
Function |
---|
function example($a = ['hello' =~ /(h)(.*)/, $1, $2], $b = $1) { } |
When called like this... | the parameters are set like this: |
---|---|
example() |
$a = [true, 'h', 'ello'] $b = undef |
Function |
---|
function example($a=['hello' =~ /(h)(.*)/, $1, $2], $b=['hi' =~ /(h)(.*)/, $1, $2], $c=$1) { } |
When called like this... | the parameters are set like this: |
---|---|
example() |
$a = [true, 'h', 'ello'] $b = [true, 'h', 'i'] $c = undef |
Function |
---|
function example($a = ['hi' =~ /(h)(.*)/, $1, if 'foo' =~ /f(oo)/ { $1 }, $1, $2], $b = $0) { } |
When called like this... | the parameters are set like this: |
---|---|
example() |
$a = [true, 'h', 'oo', 'h', 'i'] $b = undef |
Note that the top level match scope is restored at index 3, and empty again for the next parameter ($b
).
Function |
---|
'foo' =~ /(f)(o)(o)/ function example($a = $0 { } |
When called like this... | the parameters are set like this: |
---|---|
function caller() { 'foo' =~ /(f)(o)(o)/ example() } caller() |
$a = undef |
function caller() { example() } 'foo' =~ /(f)(o)(o)/ caller() |
$a = undef |
These Function Definitions | All result in error |
---|---|
function example($a = $x = $10) { } |
Assignment not allowed here |
function example($a = [$x = 10]) { } |
- " - |
function example($a = $a) { } |
- " - |
function example($a = ($b = 3), $b = 5) { } |
- " - |
function example($a = 10, $b = ($a = 10)) { } |
- " - |
function example(
$a = [1,2,3],
$b = 0,
$c = $a.map |$x| { $b = $x; $b * $a.reduce |$x, $y| {$x + $y}}
) { }
example()
results in:
$a => [1,2,3]
$b => 0
$c = [6, 12, 18]
function example($a = case "hello" {
/(h)(.*)/ : {
[1,2,3].map |$x| { "$x-$2" }
}
})
example()
results in:
$a = ["1-ello", "2-ello", "3-ello”]
function example($a = "hello",
$b = [1,2,3].map |$x| { "$x-$a" })
}
example()
results in:
$a = ["1-hello", "2-hello", "3-hello”]
function example($a = "hello" =~ /.*/) {
notify { test: message "Y$0es" }
}
example()
results in: A notify “test” with message “Yes”
function example(
$a = "hello".match(/(h)(.*)/),
$b = $a[0],
$c = $a[1]
) { }
example()
results in:
$a == ['hello', 'h', 'ello']
$b == 'hello'
$c == 'h'
define example($a) { }
example { 'test':
a => 10,
a => 20,
}
result:
error, duplicate specification of parameter $a
define example($a, $b=$a) { }
example { test: a=>10 }
define example($a=5, $b=$a) { }
example { test: a=>10 }
define example($a=10, $b=$a) { }
example { test: }
In all the examples, the result would be:
$a = 10
$b = 10
This works the same way for class
, with the additional rules when there is a value bound to $a
in data binding:
- the bound value is used when an argument was not given
- a default expression is then not evaluated
There are issues when referencing variables that do not exist in the parameter list, but exist in an outer scope.
Functions can only access global scope and fully qualified variables referencing class parameters for evaluated classes. Such references are subject to evaluation order and should be avoided.
Functions cannot access variables in node scope.
class foo {
$bar = '$bar in foo'
}
include foo
$surprise = '$surprise in top scope'
node default {
$surprise = '$surprise in node scope'
}
function example($a = $surprise, $b = $foo::bar) {
notice $a
notice $b
}
example()
Would notice:
$surprise in top scope
$bar in foo
define example($a = $title) { }
example { 'hello' : }
results in:
$a == 'hello'