Also, if an implementation elect to go fully-functional, i.e. all linear-updating procedures are just aliases of functional counterparts, then transient->persistent and persistent->transient can just be identities.

In this model, do we still need '*-copy'?  Copying functional, persistent struct won't make sense.  Copying transient struct also won't make sense, for the transient object may be invalided as soon as it is passed to '*-copy', if we keep the affine type semantics.  If the code wants to hold onto the intermediate state obtained as a transient struct, it must call transient->persistent, keep the reference to the persistent one, then call persistent->transient on it if it wants to pass the struct to a linear-updating procedure.


On Tue, Jun 1, 2021 at 8:01 PM Shiro Kawai <xxxxxx@gmail.com> wrote:
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?