Request for Feedback: Optional object type attributes with defaults in v1.3 alpha

Hi @lorengordon,

There is not a mechanism exactly equivalent to “nullable”, because nullable was a compromise we were forced to take in order to remain backward-compatible with modules written before it existed.

Specifically, there is one case that has no equivalent in optional attributes: distinguishing between a caller explicitly assigning null and a caller just omitting an optional argument entirely. We consider it a historical design error that it was possible to distinguish those cases; assigning a null value to an optional input variable that specified a default should’ve caused the final value to be that default. nullable = false effectively opts in to that correct behavior, and disables the incorrectly-designed behavior.

For optional attributes, there is intentionally no way to distinguish those situations, because there is no regrettable existing behavior to be backward compatible with in this case; the caller explicitly assigning null is exactly equivalent to omitting the attribute altogether, which matches how arguments in resource blocks work. If the optional attribute has no default value then in both cases the attribute’s value will be null, and if there is a default then in both cases the attribute’s value will be the specified default value.

To say that in more concrete terms, the optional attribtues syntax allows you to specify behavior equivalent to the following two situations:

# This one is equivalent to an optional
# attribute with a default value:
#    a = optional(string, "foo")
variable "a" {
  type    = string
  default  = "foo"
  nullable = false
}

# This one is equivalent to an optional
# attribute without a default value:
#    b = optional(string)
variable "b" {
  type    = string
  default = null
  # nullable = true is the default
}

# There is no equivalent to the following
# for an optional attribute, because this
# will produce "foo" if c is omitted, and
# null if the caller writes c = null .
variable "c" {
  type    = string
  default = "foo"
  # nullable = true is the default
}

# And for completeness, this last combination
# isn't valid for either variables or optional
# attributes:
variable "d" {
  type    = string
  default  = null
  nullable = false
  # INVALID because default = null contradicts
  # nullable = false .
}