On Mon, May 31, 2021 at 8:14 PM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:

Thank you for this example. Would it help if one of the copy operations is a no-op? For clarity, let's call the two copy operations (one in each direction) transient->persistent and persistent->transient. The latter procedure creates a genuine copy. The semantics of the former, however, is that it invalidates its argument (because it is acting on a transient structure). This means that it can become a no-op because modifying its input afterward would be an error because it is seen as being invalidated.

So, the library API accepts persistent input, and it uses persistent->transient on input if it wants to take advantage of linear-updating updaters in it:

   (define (my-library-function input)
        (... (update! (persistent->transient input))))

And the caller calls transient->persistent if it wants to pass transient object to my-library-function:

    (my-library-function (transient->persistent (update! object)))

If the library wants to provide faster, non-copying version, it can expose transient-accepting linear-updater as well:
    (define (my-library-function! input)
        (... (update! input)))

Yeah, this is clean.  A strictly-checking implementation can raise an error seeing persistent/transient flag in object, if it desires.  Loosely-checking implementation just use copy for transient->persistent and identity for persistent->transient.

Am I understanding it correctly?