An R6 instance has no json_state() method of its own, because what
an R6 class guarantees is what its methods say rather than what its
bindings happen to hold: a private field is private precisely because
it is not part of that contract. Writing one is therefore refused,
naming the class and the method it wants.
Arguments
- x
The
R6instance whose bindings are to be recorded.- class
Empty object carrying the recorded class vector, which
json_revive()dispatches on.- state
Whatever
r6_state()returned.
Value
The r6_state() function returns a list carrying package,
public and private, and r6_restore() the rebuilt instance.
Details
A class author is the party who knows whether a field is stored or
derived, whether initialize establishes an invariant, and whether a
reference should be recorded as a key rather than a value. One who has
made that judgment and wants the instance recorded as its bindings
anyway opts in with one method each way:
json_state.MyClass <- function(x) r6_state(x)
json_revive.MyClass <- function(class, state) r6_restore(class, state)The pair records the class's package alongside the public and private
bindings an instance holds, leaving out anything the generator chain
declares as a method and anything bound actively. On the way back the
generator is found again by name, an instance is allocated from a twin
of it whose initialize does nothing, and the recorded bindings are
written into that.
What the pair does not guarantee
The mechanism reaches past the interface the class offers, which is what makes it the author's call rather than the default. Four consequences are worth stating outright.
Bindings are reinstated past initialize. The instance that comes back
is filled from the document rather than constructed, so an invariant
initialize establishes is not re-established and a resource it
acquires is not acquired. A finalize method still registers on the
object, and so runs against a handle it never held.
The round trip is exact only while the class's shape is unchanged. Where the generator locks its instances, recorded state the class no longer declares has nowhere to go and is dropped with a warning naming it; a field the class has gained since arrives at its default. The lock itself comes from the generator, so one placed on a single object or binding by hand is not recorded and does not come back.
A revived instance is a new environment, so the trip holds up to the
equivalence vignette("design") states for an environment recorded by
its contents rather than under identical().
Both halves need the generator to be findable by name in the environment the class was defined in, which is checked on the way out as well as on the way in. A class defined inside a function, one whose name finds two generators, and a non-portable class are all refused where they are written.
Examples
Counter <- R6::R6Class("Counter",
public = list(
n = 0,
initialize = function(n = 0) self$n <- n,
bump = function() {
self$n <- self$n + 1
invisible(self)
}
)
)
json_state.Counter <- function(x) r6_state(x)
json_revive.Counter <- function(class, state) r6_restore(class, state)
counter <- Counter$new()$bump()$bump()
json_read_str(json_write_str(counter))$n
#> Error: an `R6` instance needs a `json_state()` method for class `Counter` at `x`