On Tuesday, 29 November 2022 at 18:59:46 UTC, DLearner wrote:
>To me, it appears that there are really two (entirely separate) concepts:
A. Supporting the useful concept of variable length (but otherwise entirely conventional) arrays;
B. Supporting a language feature that acts as a window to an array, through which that array can be manipulated.
And currently these two concepts are combined.
Suggestion: it would be clearer if the two concepts were separated:
- Convert 'int[] VarArr;' so it produces a straightforward value-type variable array, called 'VarArr';
- Implement a new concept 'int slice Window;' to produce an object of type 'int slice', called 'Window'.
'Window' is a 'slice' into an int array, not an array itself or even a variable.
Opinions?
I have implemented that in styx.
- You have the type for dynamic arrays, called TypeRcArray, syntax is
Type[+]
- You have the type for slices (what you describe as a window), syntax is
Type[]
but it is mostly obtained using expressions, e.gmySlice = myRcArray[lo .. hi]
or
myStaticArray[lo .. hi]
ormyPointer[lo .. hi]
.
This sounded like a good idea but it has appeared very quickly that slices are not so useful, especially when management is based on reference counting because then slices requires a form of management too. Here is why:
Main caracteristics of a slice are
- they cannot modify the identity of their sources. The identity of the source is what makes the integrity of a dynamic array, what makes their references countable. So it is the content pointer and the length. In consequence you cannot change the length of the source, you can only reduce the view. You can change the elements in the view.
- the length and the pointer are cached as a value on the stack while for a dynamic array this is stored before that data, on the heap.
Problems start happening when you escape a slice
struct S
{
var s32[] memberSlice;
}
function f(var S s): auto
{
var s32[+] a = (new s32[+])(2);
// problem 1 : `s` lifetime > `a` lifetime
s = (a[]).tupleof; // note: tuples are used in pace of struct literals
// problem 2
return a[1 .. $]; // a is automatically decref'd on return
// so the caller pulls a dead heap block.
}
Essentially slices are only useful to be consumed locally, typically
while mySlice.length do
{
slice = slice[1..$];
}
And that's it. So at first glance slices are some cool, simplified, functionally stripped down arrays but they introduce new problems, at least when management of dynamic arrays is based on reference counting. Those new problems can only be solved using lifetime analysis (so a compile-time check... still better than runtime ref counting however).