What is a "copy"?

It might sound like an "easy" answer… but I am struggling to find where the definition of this might be documented.

For example:

This tells me that a type which is Copyable is a type that can be copied… but then what does that mean?

There might be some more clues in SE-0390:

All currently existing types in Swift are copyable, meaning it is possible to create multiple identical, interchangeable representations of any value of the type.

But then what does that mean? Are "identical" and "interchangeable" defined anywhere else across Swift? I don't see much more discussion about what that means or what that implies.

If we try and work to a formal definition of this:

if b = copy(a) then a.isIdentical(to: b)
if b = copy(a) then a.isInterchangable(to: b)

Unstated and undefined are axioms of Reflexivity:

a.isIdentical(to: a) is always true
a.isInterchangable(to: a) is always true

Symmetry:

a.isIdentical(to: b) implies b.isIdentical(to: a)
a.isInterchangable(to: b) implies b.isInterchangable(to: a)

and Transitivity:

a.isIdentical(to: b) and b.isIdentical(to: c) implies a.isIdentical(to: c)
a.isInterchangable(to: b) and b.isInterchangable(to: c) implies a.isInterchangable(to: c)

If the definition of Copyable presented in SE-0390 was meant to imply that a representation that is identical must be interchangeable and a representation that is interchangeable must be identical that would simplify things a little… but still AFAIK leaves some more basic questions unanswered. For example: if a representation is identical and interchangeable does this imply that a representation is a copy? Or are some representations that are identical and interchangeable not copies?

A slightly different idea is presented in SE-0426:

[Types that are "bitwise-copyable"] can be moved or copied with direct calls to memcpy and […] require no special destroy operation.

I believe the unstated implication is that a copy produced by memcpy must then compare equal using memcmp:

if b = bitwiseCopy(a) then memcmp(a, b)

But I see no mention in SE-0426 of whether or not we consider a and b to be identical or interchangeable. A "future direction" does suggest that "BitwiseCopyable could be defined as the composition of several protocols":

typealias BitwiseCopyable = Bitwise & Copyable & DefaultDeinit

But I don't see where we currently "inherit" the concepts of a copy being identical and interchangeable from Copyable on BitwiseCopyable.

Another slightly different idea is presented in the definition of Equatable:

If we accepted the axiom of Reflexivity on our original identical and interchangeable properties and we add the axiom of Reflexivity from Equatable that seems to imply that a representation that is identical and interchangeable is also "substitutable". But this doesn't help me much to look for a definition of identical or interchangeable on a type that is not Equatable. There also seems to be a subtle implication here that it would be possible for a representation to be substitutable and not identical and interchangeable.

Could there be any other place in the repos where there might be more documentation here on these topics? Would there have been any evolution proposals that spent more time on these questions?

For a Copyable type, copies are literally interchangeable with each other without changing the surface meaning of the program (though obviously can affect the performance characteristics). If you have

foo(a)

then

let b = copy a
foo(b)

will behave exactly the same. The compiler by this principle is free to insert or remove copies to improve performance or maintain language semantics.

6 Likes

This is not implied, nor is it implied in C where memcpy() originates. Values may contain padding bytes or even padding bits of undefined value, and those bytes may on some CPU architectures (hi Alpha) not even contain valid bit patterns representable as bytes!

3 Likes

If a value’s type is Copyable, you can make a copy of the value, and this copy has its own lifetime independent of the original value.

The primitive operation of copying a value could do anything (if you have an imported C++ type with a copy constructor) but for types defined in Swift, the copy is always bitwise identical to the original value, but while copying we have to update reference counts.

Now, your question was perhaps asking for a precise definition of the sense in which the original value and the copy are “identical”. I don’t think there’s a really formal definition here because at the end of the day, operations on the value could read alignment padding, or look at the address of the value, and then the copy is definitely not going to behave the same.

But to get around the lack of equality, you can say this. For every pure “value semantics” function f that takes your value’s concrete type and returns a Bool, you should have f(x) == f(copyOfX). So if two values cannot be distinguished by any property you can test, then they are identical or interchangeable in the sense meant by the proposal.

4 Likes