1# Value Marks
2
3----
4
5**Note:** Marks are an optional feature that will not be needed in most
6applications. If your application never uses this API then you don't need to
7worry about encountering marked values, and you can ignore this document.
8
9----
10
11A `cty.Value` can optionally be _marked_, which causes it to carry around some
12additonal metadata along with its value. Marks are just normal Go values that
13are value to use as map keys, and are compared by equality.
14
15For example, an application might use marks to track the origin of a particular
16value in order to give better error messages, or to present the value in a
17different way in a UI.
18
19```go
20// Use a named type for all marks, for namespacing purposes.
21type fromConfigType int
22val fromConfig fromConfigType
23
24return val.Mark(fromConfig)
25```
26
27```go
28if val.HasMark(fromConfig) {
29    // Maybe warn the user that the value is derived from configuration?
30    // Or whatever makes sense for the calling application.
31}
32```
33
34When a value is marked, operation methods using that value will propagate the
35marks to any result values. That makes marks "infectious" in the sense that
36they propagate through operations and accumulate in the result automatically.
37
38However, marks cannot propagate automatically thruogh _integration_ methods,
39and so a calilng application is required to explicitly _unmark_ a value
40before using them:
41
42```go
43val, valMarks := val.Unmark()
44// ...then use integration methods with val,
45// eventually producing a result that propgates
46// the marks:
47return result.WithMarks(valMarks)
48```
49
50## Marked Values in Sets
51
52Sets present an interesting problem for marks because marks do not contribute
53to equality of two values and thus it would be possible in principle to add
54the same value to a given set twice with different marks.
55
56To avoid the complexity of tracking superset marks on a per-element basis,
57`cty` instead makes a compromise: sets can never contain marked values, and
58if any marked values are passed to `cty.SetVal` then they will be automatically
59unmarked and the superset of all marks applied to the resulting set as a whole.
60
61This is lossy about exactly which elements contributed which marks, but is
62conservative in the sense that any access to elements in the set will encounter
63the superset marks as expected.
64
65## Marks Under Conversion
66
67The `cty/convert` package is aware of marks and will automatically propagate
68them through conversions. That includes nested values that are marked, which
69will be propagated to the corresponding nested value in the result if possible,
70or will be simplified to marks on a container where an exact propagation is not
71possible.
72
73## Marks as Function Arguments
74
75The `cty/function` package is aware of marks and will, by default,
76automatically unmark all function arguments prior to calling a function and
77propagate the argument marks to the result value so that most functions do
78not need to worry about handling marks.
79
80A function may opt in to handling marks itself for a particular parameter by
81setting `AllowMarks: true` on the definition of that parameter. If a function
82opts in, it is therefore responsible for correctly propagating any marks onto
83its result.
84
85A function's `Type` implementation will receive automatically-unmarked values
86unless `AllowMarks` is set, which means that return-type checking alone will
87disregard any marks unless `AllowMarks` is set. Because type checking does not
88return a value, there is no way for type checking alone to communicate which
89marks it encountered during its work.
90
91If you're using marks in a use-case around obscuring sensitive values, beware
92that type checking of some functions could extract information without
93preserving the sensitivity mark. For example, if a string marked as sensitive
94were passed as the first argument to the stdlib `JSONDecode` function then
95type-checking of that function will betray the object property names inside
96that value as part of the inferred result type.
97
98## Marks Under Serialization
99
100Marks cannot be represented in either the JSON nor the msgpack serializations
101of cty values, and so the `Marshal` functions for those will return errors
102if they encounter marked values.
103
104If you need to serialize values that might contain marks, you must explicitly
105unmark the whole data structure first (e.g. using `Value.UnmarkDeep`) and then
106decide what to do with those marks in order to ensure that if it makes sense
107to propagate them through the serialization then they will get represented
108somehow.
109