1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! # Operations to Replace Objects
//!
//! Most objects reference other objects, which can in turn reference even more
//! objects. All of these objects form a graph. Objects are immutable, which
//! means changing one in any way means creating a new version of the object,
//! and hence a new version of the objects that reference it, all the way to the
//! root of the graph.
//!
//! Replace operations replace objects within the object graph, solving a few
//! problems that would otherwise occur here:
//!
//! 1. They take care of *finding* the object. A replace operation for a given
//! object can be called on any object that references it, directly or
//! indirectly, so the caller does not need to know which objects reference
//! the called object directly.
//! 2. They take care of creating new versions of all objects referencing the
//! replaced object in the whole object graph defined by the object that the
//! replace operation is called on.
//! 3. They *only* create a new version of an object, if anything has actually
//! been replaced.
//!
//!
//! ## Structure
//!
//! All replace operations follow the same structure:
//!
//! - They are implemented for `Handle<T>`, where `T` is a bare object type.
//! - They have an associated type to identify `T`. See implementation note
//! below.
//! - They take a reference to the [`Handle`] of the original object that
//! should be replaced.
//! - Based on the specific replace operations, they take the [`Handle`] of the
//! replacement, or multiple handles that replace the object. (Depending on
//! the arity of the reference.)
//! - If the original object is referenced (directly or indirectly) by the
//! object the operation is called on, it is replaced with the replacement. If
//! not, nothing happens.
//! - They return an enum that indicates whether an object was actually
//! replaced. If it was, it contains the [`Handle`] to the new version of the
//! object the operation was called on. If it wasn't, it contains the original
//! handle.
//!
//!
//! ## Comparison to Update Operations
//!
//! There is another type of operation, [update operations], which has some
//! conceptual overlap with replace operations. There are some differences
//! though:
//!
//! - Each update operation is only implemented for one type of object
//! respectively, the one it updates.
//! - Update operations cover changes to attributes that are not references to
//! other objects.
//! - Update operations cover changes to references that are not replacements,
//! like adding more references.
//! - Update operations might provide more convenient methods to replace an
//! object, if the object they are implemented on references only one such
//! object. In such a case, the update operation does not need to take a
//! reference to the object being updated, while the respective replace
//! operation still does.
//!
//!
//! ## Implementation Notes
//!
//! Only a few replace operations are implemented so far. More can be added, as
//! the need arises.
//!
//! As of this writing, replace operations are generally implemented in the most
//! simple and naive way possible: Iterating over all referenced objects and
//! calling the replace operation recursively. This might have performance
//! implications for large object graphs.
//!
//! There are some update operations that are straight-up redundant with what
//! replace operations are doing. Some of the methods even have the same names.
//! Those haven't been removed yet, as update operations generally require a
//! reference to a bare object, while replace operations require a `Handle`.
//! There are some open questions about how operations in general should deal
//! with objects being inserted or not, so it's probably not worth addressing
//! this before doing a general revamp of how operations deal with inserting.
//!
//! All replace traits have an associated type. This is required, because the
//! traits are implemented for `Handle<T>`, but they must refer to the bare
//! object type (`T`) in their method return values. It should be possible to
//! avoid this additional complexity by implementing the traits for `T`
//! directly, but then we would need arbitrary self types to keep the current
//! level of convenience:
//! <https://doc.rust-lang.org/beta/unstable-book/language-features/arbitrary-self-types.html>
//!
//!
//! [`Handle`]: crate::storage::Handle
//! [update operations]: crate::operations::update
pub use ;
/// The output of a replace operation
///
/// See [module documentation] for more information.
///
/// [module documentation]: self