139 lines
6.8 KiB
Markdown
139 lines
6.8 KiB
Markdown
|
# `cty` Concepts
|
||
|
|
||
|
`cty` is built around two fundamental concepts: _types_ and _values_.
|
||
|
|
||
|
A _type_ represents a particular way of storing some data in memory and a
|
||
|
set of operations that can be performed on that data. A _value_ is, therefore,
|
||
|
the combination of some raw data and a _type_ that describes how that data
|
||
|
should be interpreted.
|
||
|
|
||
|
The simplest types in `cty` are the _number_, _string_ and _bool_ types,
|
||
|
collectively known as the _primitive_ types.
|
||
|
|
||
|
Along with the primitive types, `cty` supports _compound_ types, which are
|
||
|
types that are constructed by assembling together other types in a particular
|
||
|
way. The _compound_ types are further subdivided into two categories:
|
||
|
|
||
|
* **Collection Types** represent collections of values that all have the same
|
||
|
type (the _element type_) and permit access to those values in different
|
||
|
ways. The collection type kinds are _list_, _set_, and _map_.
|
||
|
|
||
|
* **Structural Types** represent collections of values that may all have
|
||
|
_different_ types, organized either by name or by position in a sequence.
|
||
|
The structural type kinds are _object_ and _tuple_.
|
||
|
|
||
|
For example, "list of string" is a collection type that represents a
|
||
|
collection of string values (_elements_) that are each assigned a sequential
|
||
|
index starting at zero, while "map of string" instead assigns each of its
|
||
|
elements a name in the form of a string value.
|
||
|
|
||
|
The details of the specific types and type kinds are covered in
|
||
|
[the full description of the type system](./types.md); the remainder of _this_
|
||
|
document will discuss types and values in general, using specific types only
|
||
|
as examples.
|
||
|
|
||
|
## Value Operations
|
||
|
|
||
|
Each type defines a set of operations that are valid on its values. For
|
||
|
example, the _number_ type permits various arithmetic operations such as
|
||
|
addition, subtraction, and multiplication, but these are not permitted for
|
||
|
other types such as _bool_.
|
||
|
|
||
|
Since `cty` is a dynamic type system (from the perspective of the calling Go
|
||
|
program), the validity of an operation on a given value must be checked at
|
||
|
runtime. The documentation for each type defines what operations are valid
|
||
|
on it and what semantics each operation has.
|
||
|
|
||
|
## Unknown Values and the Dynamic Pseudo-Type
|
||
|
|
||
|
`cty` has some additional _optional_ concepts that may be useful in certain
|
||
|
applications.
|
||
|
|
||
|
An _unknown value_ is a value that carries a type but no value. It can serve
|
||
|
as a placeholder for a value to be resolved later, which can be useful when
|
||
|
implementing a static type checker for a language. Unknown values are special
|
||
|
because they support the same operations as a known value of the same type
|
||
|
but the result will itself be an unknown value. For example, the number 5
|
||
|
added to an unknown number yields another unknown number.
|
||
|
|
||
|
The dynamic pseudo-type is a special type that serves as a placeholder for
|
||
|
a type that isn't yet known. Whereas unknown values represent situations where
|
||
|
the type is known and the value is not, the dynamic pseudo-type represents
|
||
|
situations where neither is known, or where any value of any type is permitted.
|
||
|
It is referred to as a "pseudo-type" because while it can be used in many
|
||
|
places where types are permitted, it does not define any operations of its own.
|
||
|
|
||
|
These two concepts are related in that the dynamic pseudo-type has no non-null,
|
||
|
non-unknown values. It single non-null type is itself an unknown value.
|
||
|
_All_ operations are supported on non-null dynamic values, but the result
|
||
|
will always be an unknown value, possibly type-unknown itself.
|
||
|
|
||
|
Dealing with unknown values and the dynamic pseudo-type can cause additional
|
||
|
complexity for a calling application, although many details of it are handled
|
||
|
automatically by the `cty` internals. As a consequence, the main `cty` API
|
||
|
promises to never produce an unknown value for an operation unless one of the
|
||
|
operands is itself unknown, and so applications can opt out of this additional
|
||
|
complexity by never providing unknown values as operands.
|
||
|
|
||
|
## Type Equality and Type Conformance
|
||
|
|
||
|
Two types are said to be equal if they are exactly equivalent. Each type kind
|
||
|
defines its own equality rules, but the overall intent is to implement strict
|
||
|
type comparisons.
|
||
|
|
||
|
Type _conformance_ is a slightly-weaker concept that allows the dynamic
|
||
|
pseudo-type to be used as a placeholder to represent "any type". Therefore
|
||
|
a given type is equal only to itself but it is _conformant_ to either itself
|
||
|
or the dynamic pseudo-type.
|
||
|
|
||
|
Type conformance is not directly used by `cty`'s core, but it is used as
|
||
|
a building block for the `function` package and for JSON serialization.
|
||
|
|
||
|
## The `cty` Go API
|
||
|
|
||
|
The primary way a application works with `cty` values is via the API exposed
|
||
|
by the `cty` go package. The full details of this package are in
|
||
|
[its reference documentation](https://godoc.org/github.com/apparentlymart/go-cty/cty),
|
||
|
so this section will just cover the basic usage patterns.
|
||
|
|
||
|
The main features of the `cty` package are the Go types `cty.Type` and `cty.Value`,
|
||
|
which each represent the concept they are named after.
|
||
|
|
||
|
The package contains variables that represent the primitive types, `cty.Number`,
|
||
|
`cty.String` and `cty.Bool`. It also contains functions that allow the
|
||
|
construction of compound types, such as `cty.List`, `cty.Object`, etc. These
|
||
|
functions each take different arguments depending on the kind of compound type
|
||
|
in question.
|
||
|
|
||
|
Alongside the types and type factories, the package also contains variables
|
||
|
and functions for constructing _values_ of these types, which conventionally
|
||
|
have names that are the corresponding type or type kind with the suffix `Val`.
|
||
|
For example, the two boolean values are exported as `cty.True` and `cty.False`,
|
||
|
and string values can be constructed using the function `cty.StringVal`, given
|
||
|
a native Go string.
|
||
|
|
||
|
The `cty.Type` and `cty.Value` types are similar to the types of the same
|
||
|
name in the built-in Go `reflect` package. They expose methods that are the
|
||
|
union of all operations supported across all types, but each method has a
|
||
|
set of constraints associated with it, and failure to follow these will result
|
||
|
in a run-time panic.
|
||
|
|
||
|
The `cty.Value` object has two classes of methods:
|
||
|
|
||
|
* **Operation Methods** stay within the `cty` type system, dealing entirely
|
||
|
with `cty.Value` instances. These methods fully deal with concerns such as
|
||
|
unknown values, so the caller just needs to be sure to apply only operations
|
||
|
that are valid for the receiving value's type.
|
||
|
|
||
|
* **Integration Methods** live on the boundary between `cty` and the native
|
||
|
Go type system, and can be used by the calling application to integrate
|
||
|
with non-`cty`-aware code. These methods often have constraints such as not
|
||
|
supporting unknown values, which are covered in their documentation.
|
||
|
|
||
|
While the integration methods alone are sufficient for a calling application
|
||
|
to convert to and from `cty` values, the utility package
|
||
|
[`gocty`](./gocty.html) provides a more convenient way to convert between
|
||
|
Go native values and `cty` values.
|
||
|
|
||
|
|