But that isn’t going to work on, for example, Deque, or any Indexable that isn’t implemented as a contiguous area of memory.
I don’t think adding a couple of named arguments can be considered “littering”, especially those. In Common Lisp, for instance, almost all sequence functions have :start and :end keyword arguments and they are very useful. It would be a little more complicated in Crystal because both start/count and range are used to express subsequences, but consistently having the signature of every Indexable[::Mutable] method that doesn’t modify the object’s size end in
Slice idea still looks more elegant to me from both point of usage and point of implementation. Maybe slice for Indexable can return “virtual slice” that forward calls of unsafe_put and unsafe_fetch to parent object.
That’s a good solution, too. It’s less foolproof tho, since the slices (even this virtual slice) can be invalidated inside the block. But we aren’t fools, are we? ;)
I wonder if the Crystal team would consider adding this (or the extra parameters) to the stdlib…
This approach is certainly an interesting solution.
A concern with this generalization is that it doesn’t directly profit from optimizations in the underlying container’s implementation.
To pick up on your example, Array#rotate! overrides Indexable::Mutable#rotate! using a more efficient algorithm that takes specifics of its data buffer into account.
I suppose we could move these optimizations to VirtualSlice. But that seems impractical, especially when considering integration of container types outside stdlib.
If we pass the intended range directly as parameter to the method, performance optimizations could be trivially used.
I wonder if it’s possible to have a safe slice, that would raise of the content is invalid. But then, it might just be better performance wise to split the array, work on the sub -part, and then rejoin.
A language feature to forbid the VirtualSlice in the block from being captured would be nice, i.e. RFC 4 as a semantic directive rather than an optimization opportunity. IIRC Swift has something like that
Multi-threading/concurrency safety is a completely separate issue though. It’s not specific to this use case. Any existing method on Array is exposed to that exactly like any additional ones we’re discussing here.
I suppose we could move these optimizations to VirtualSlice.
I think that for Array (and other containers that can guarantee that memory is contigious ) #slice can yield Slice instead of VirtualSlice. Optimizations for Slice are already applied.