-
Notifications
You must be signed in to change notification settings - Fork 14
Description
splice : Int -> Int -> Array a -> Array a -> Array a
The OG ultimate array manipulation function. Start at an index, remove a number of items and insert items in their place.
example : Array Int
example =
Array.fromList [ 1, 2, 3, 4, 5 ]
splice 2 2 Array.empty example —-> Array.fromList [1, 2, 5]
splice 2 0 (Array.singleton 33) —-> Array.fromList [ 1, 2, 33, 4, 5 ]
Motivating use case
- Porting. Since this function exists in most languages, it often comes up when porting code.
- various use cases that involve maintaining a sorted array as more items are added into the array
- Useful primitive to build other functions.
Alternative designs
a : Int -> Int -> Array a -> Array a -> ( Array a, Array a )
b : Int -> Int -> (Array a -> Array a) -> Array a -> Array a
c : Int -> Int -> List a -> Array a -> Array a
d: { start: Int, deleteCount: Int, insert: Array a } -> Array a -> Array a
a) returns the deleted items. This is rarely useful (as you can easily get the items to be deleted with a slice call) and suffers from ambiguity on which are the deleted items and which is the returned array.
b) allows the inserted items to depend on the deleted items. If you think of splice as a sort of generalised CRUD function over arrays, then this version gives you the Update. It also composes nicely with Array.map to implement a map on only a part of an array. I personally quite like this version. However, for a lot of the normal use cases it introduces a fair amount of ceremony. Also getting the deleted range is again trivial with a slice, so not particularly necessary to build it like this.
c) is a lot nicer for literal use cases. I don’t really have a way to evaluate this, but I think the type mixing is aesthetically not particularly nice. But if there was a strong argument for this, I would be willing to consider it.
d) this base signature suffers from having pairs of arguments with different meanings where the programmer just needs to know the order. This version disambiguates the call sites. I think this is somewhat mitigated in that this is a well known kind of function and the argument order tends to be like this. Also we don’t have much precedent in -extra for this style of API.
Rationale
If this didn’t exist, how would one implement this using existing functionality?
splice start delete insert array =
Array.slice (start + delete) (Array.length array) array
|> Array.append insert
|> Array.append (Array.slice 0 start)
or some such. I’m not particularly confident in the correctness of this implementation and neither do I think that its intent is particularly obvious. I would also be wondering about performance and whether this is indeed a decent implementation. As such I think having it in a well tested and well performing library would be preferable.