1# Converting between `cty` types 2 3[The `convert` package](https://godoc.org/github.com/zclconf/go-cty/cty/convert) 4provides a standard set of type conversion routines for moving between 5types in the `cty` type system. 6 7_Conversion_ in this context means taking a given value and producing a 8new value of a different type that, in some sense, contains the same 9information. For example, the number `5` can be converted to a string as 10`"5"`. 11 12Specific conversion operations are represented by type `Conversion`, which 13is a function type that takes a single value as input and returns a value 14or an error. 15 16## "Safe" and "Unsafe" conversions 17 18The `convert` package broadly organizes its supported conversions into two 19types. 20 21"Safe" conversions are ones where all values of the source type can 22be represented in the target type, and thus the conversion is guaranteed to 23succeed for any value of the source type. 24 25"Unsafe" conversions, on the other hand, are able to convert only a subset 26of values of the source type. Values outside of that subset will cause the 27conversion function to return an error. 28 29Converting from number to string is safe because an unambiguous string 30representation can be created for any number. The converse is _unsafe_, 31because while a string like `"2.5"` can be converted to a number, a string 32like `"bananas"` cannot. 33 34The calling application must choose whether to attempt unsafe conversions, 35depending on whether it is willing to tolerate conversions returning errors 36even though they ostensibly passed type checking. Operations that have both 37safe and unsafe modes come in couplets, with the unsafe version's name 38having the suffix `Unsafe`. 39 40## Getting a Conversion 41 42To find out if a conversion is available between two types, an application can 43call either `GetConversion` or `GetConversionUnsafe`. These functions return 44a valid `Conversion` if one is available, or `nil` if not. 45 46Note that there are no conversions from a type to itself. Callers should check 47if two types are equal before attempting to obtain a conversion between them. 48 49As usual, `cty.DynamicPseudoType` serves as a special-case placeholder. It is 50used in two ways, depending on whether it appears in the source or the 51destination type: 52 53* When a source type is dynamic, a special unsafe conversion is available that 54 takes any value and passes it through verbatim if it matches the destination 55 type, or returns an error if it does not. This can be used as part of handling 56 dynamic values during a type-checking procedure, with the generated 57 conversion serving as a run-time type check. 58 59* When a _destination_ type is dynamic, a simple passthrough conversion is 60 generated that does not transform the source value at all. This is supported 61 so that a destination type can behave similarly to a type description used 62 for a conformance check, thus allowing this package to be used to attempt 63 to _make_ a type conformant, rather than merely check whether it already 64 is. 65 66## Converting a Value 67 68A value can be converted by passing it as the argument to any conversion whose 69source type matches the value's type. If the conversion is an unsafe one, the 70conversion function may return an error, in which case the returned value is 71invalid and must not be used. 72 73As a convenience, the `Convert` function takes a value and a target type and 74returns a converted value if a conversion is available. This is equivalent 75to testing for an unsafe conversion for the value's type and then immediately 76calling any discovered conversion. An error is returned if a conversion is not 77available. 78 79## Type Unification 80 81A related idea to type _conversion_ is type _unification_. While conversion 82is concerned with going from a specific source type to a specific target type, 83unification is instead concerned with finding a single type that several other 84types can be converted to, without any specific preference as to what the 85final type is. 86 87A good example of this would be to take a set of values provided to initialize 88a list and choose a single type that all of those values can be 89converted to, which then decides the element type of the final list. 90 91The `Unify` and `UnifyUnsafe` functions are used for type unification. They 92both take as input a slice of types and then return, if possible, a single 93target type along with a slice of conversions corresponding to each 94of the input types. 95 96Since many type pairs support type conversions in both directions, the unify 97functions must apply a preference for which direction to follow given such a 98pair of types. These functions prefer safe conversions over unsafe ones 99(assuming that `UnifyUnsafe` was called), and prefer lossless conversions 100over potentially-lossy ones. 101 102Type unification is a potentially-expensive operation, depending on the 103complexity of the passed types and whether they are mutually conformant. 104 105## Conversion Charts 106 107The foundation of the available conversions is the matrix of conversions 108between the primitive types. String is the most general type, since the 109other two primitive types have safe conversions to string. The full 110matrix for primitive types is as follows: 111 112| | string | number | boolean | 113|---------|:------:|:------:|:-------:| 114| string | n/a | unsafe | unsafe | 115| number | safe | n/a | none | 116| boolean | safe | none | n/a | 117 118The conversions for compound types are then derived from the above foundation. 119For example, a list of numbers can convert to a list of strings 120because a number can convert to a string. 121 122The compound type kinds themselves have some available conversions, though: 123 124| | tuple | object | list | map | set | 125|--------|:------:|:------:|:----:|:------:|:----------:| 126| tuple | n/a | none | safe | none | safe+lossy | 127| object | none | n/a | none | safe | none | 128| list | unsafe | none | n/a | none | safe+lossy | 129| map | none | unsafe | none | n/a | none | 130| set | unsafe | none | safe | none | n/a | 131 132Conversions between compound kinds, as shown above, are possible only 133if their respective elements/attributes also have conversions available. 134 135The conversions from structural types to collection types rely on 136type unification to identify a single element type for the final collection, 137and so conversion is possible only if unification is possible. 138 139## Conversion between Object Types 140 141There are some special considerations for conversion between distinct object 142types that do not apply to conversion between types of other kinds. 143 144For object types, `convert` implements _structural typing_ behaviors, where 145the target type is considered to be a description of a set of attributes the 146final result should have. There are two important additional concerns that 147result from this design intent: 148 149* If the input type has additional attributes that are not mentioned at all 150 in the target type, those additional attributes are silently discarded 151 during conversion, leading to a new object value that has a subset of the 152 attributes of the input value, and whose type therefore conforms to the 153 target type constraint. 154 155* If any of the attributes of the target type are marked as optional using 156 the **currently-experimental** `cty.ObjectWithOptionalAttrs` constructor, 157 type conversion will tolerate those attributes being absent in the given 158 type, and the resulting value will include appropriately-typed null value 159 placeholders as the values of those omitted attributes. 160 161 This behavior is subject to change even in future minor versions of the 162 `cty` module, so that we can try it out with experimental versions of 163 calling applications and adjust the details of the behavior if needed. 164 Hopefully this mechanism will be stabilized in a future release, if those 165 downstream experiments are successful. 166