www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D's slices have "discretionary sharing semantics"

D's slices have "discretionary sharing semantics"

This is a request for comment in defining the *existing* semantics of D's
arrays. I might have gotten a number of things wrong and appreciate your
comments.

Being a C++ programmer, and a relative D newbie, I've found it difficult to
wrap my head around D's arrays. The following is the way of describing their
semantics that makes me finally understand them.

There are two types of arrays in D:

1) Fixed-sized arrays: These have value semantics; they own their data and are
copied when assigned (even as function parameters as of dmd 2.036). They are
quite different and powerful from C's arrays and are very easy to understand.

2) "The other type of arrays": They do not own any elements; the elements that
they provide access to are owned by something else: the elements might be owned
by e.g. a fixed array or the garbage collector.

This type has "discretionary sharing" semantics (I like the term "ad-lib
sharing" as well, but the word that begins with a 'D' has more potential ;) )

The problem so far has been with the second type. The names "dynamic array" and
"slice" have been used but neither term helped me. I found none of those names
nor their definitions to be adequate to describe what they are.

First, they are not really "arrays" because they don't own their elements; they
actually "share consecutive elements" that are owned by something else.

Second, they are not "slices" in the way slices are commonly described. For
example, Wikipedia's definition contains "array slicing is an operation that
extracts certain elements from an array and packages them as another array."
D's dynamic arrays or slices don't fit this definition.

The distinction between "dynamic array" and "slice" has been very confusing
too. Aren't they the same thing? I will use "slice" from now on...

D's slices have "discretionary sharing semantics." When copied, they start
sharing elements but they never own the elements themselves. The sharing is
discretionary, because any slice

- may start sharing additional elements without breaking its existing sharing
contracts, or

- may drop their sharing contracts, and start sharing new elements

Assignment (including copying to functions) of slices do not "assign," but
start a discretionary sharing contract, which may be broken by either of the
parties.

I will try to express these semantics by modifying Table 4.3 of Andrei's
chapter on arrays. (I've also removed some text for brevity.)

(I've formatted the table to fit a 70 character wide window.)

Legend:
* - Modification to Andrei's table
X - Operations that may break the sharing contract

Name             Type   X * Description
-----------------------------------------
new T[n]         T[]      * Creates n consecutive elements and
                            returns a slice that shares those elements

[t1, . . ., tk]  T[]      * Creates k consecutive elements and
                            returns a slice that shares those
                            elements

a=b              T[]      * Copies slice to slice (a starts sharing
                            the same elements as b has been sharing)

a[‹e ›]          ref T      Accesses element by index

a[‹e1›..‹e2›]    T[]      * Returns a slice that shares some of a's
                            elements

a[]              T[]        Participate in array-wise expressions
                            otherwise just the identity operation
                            a[0 .. $]

a.dup            T[]      * Creates copies of elements that 'a' shares
                            and returns a slice that shares the new
                            elements

a.length         size_t     Reads slice's length

a.length = n     size_t X * Changes the number of elements that 'a'
                            shares; may share less of what is already
                            being shared, or may share newly created
                            elements that are the concatenation of
                            the existing elements that 'a' has been
                            sharing and new ones with the value T.init

a is b           bool     * Compares whether the slices share the same
                            elements

a !is b          bool       Same as !(a is b)

a == b           bool       Compares slices for element-for-element
                            equality

a != b           bool       Same as !(a == b)

a~t              T[]    X * Creates new elements by concatenating the
                            elements that the slice has been sharing
                            and the new element; returns a slice that
                            shares all of the new elements

t~a              T[]    X * Creates new consecutive elements by
                            concatenating the new element and the
                            elements that the slice has been sharing;
                            returns a slice that shares all of the
                            new elements

a~b              T[]    X * Creates new consecutive elements by
                            concatenating the elements that each
                            slice has been sharing; returns a new
                            slice that shares all of the new elements

a ~= t           T[]    X * Appends a new element to the elements
                            that the slice has been sharing

a ~= b           T[]    X * Appends the elements that 'b' has been
                            sharing to the elements that 'a' has been
                            sharing; 'a' starts sharing all of those
                            elements

a.ptr            T*         The address of a's first element (unsafe)


Does this make sense?

Ali
Nov 06 2009