route/vendor/github.com/zclconf/go-cty/docs/concepts.md

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.