Consider `.mutatedCopy(mutatorFunction)`
brad4d opened this issue · 4 comments
During our session today, I was struck by how painful all this bikeshedding on names is and how it'll end up having to be done for every method added in the future, too.
I felt inspired to come up with a way to have just one method and allow easy reuse of the existing, mutating methods instead of creating new ones.
Here's my proposal in the form of a polyfill for Array.
Array.prototype.mutatedCopy = function(mutatorFunction = copy => copy) {
// Make a mutable copy.
// For mutable things like Array and TypedArray, this copy will be the actual final value.
// For Tuple we will create an Array here, so the mutatorFunction can change it.
const copy = [...this];
mutatorFunction(copy);
// The Tuple version of this method will convert the copy to a Tuple here.
return copy;
};
const ary123 = [1, 2, 3];
// Note that we don't care about what the return value of our arrow function is,
// so we don't have to deal with the fact that `.push()` returns a number, not the array.
const ary123abc = ary123.mutatedCopy(copy => copy.push('a', 'b', 'c'));
// You can do any mutation you want.
const ary234 = ary123.mutatedCopy(copy => { copy.shift(); copy.push(4); } );
I'm sure some folks won't like having to define the arrow function,
but to me it doesn't seem like a big cost for the flexibility it provides.
The primary downside I see here is that this approach always creates a complete copy of the original.
With the custom methods approach the VM could theoretically avoid copying all of a large array for a call like
array.spliced(0, 500)
.
The thing is, one of the main motivations behind this is to be in alignment with Tuples, this function defined for Tuples would be pretty weird: would we have a temporary array instead of the tuple in that function? If yes, that would mean I could inject objects into the array and have a failure when we try to reconstruct the tuple, which I expect to be confusing behavior...
In any case: that looks like a good thing to do in userland similarly to what immer does. I am thinking on bringing that pattern to Record & Tuple too but I have trouble seeing how that would fit in the spec.
To be clear, naming will be tough but I do think it will be tough escaping it...
The thing is, one of the main motivations behind this is to be in alignment with Tuples, this function defined for Tuples would be pretty weird: would we have a temporary array instead of the tuple in that function?
Yes. I think you overlooked the comment in my example pointing out that for the Tuple version of the method you would have a temporary Array
to work with. The nice thing about that is you get access to all Array
functions here without having to create Tuple equivalents.
If yes, that would mean I could inject objects into the array and have a failure when we try to reconstruct the tuple, which I expect to be confusing behavior...
If you try to do tuple.pushed(mutableThing)
you would get an error, too.
Wouldn't that be equivalent to .mutatedCopy()
producing an error?
In any case: that looks like a good thing to do in userland similarly to what immer does. I am thinking on bringing that pattern to Record & Tuple too but I have trouble seeing how that would fit in the spec.
To be clear, naming will be tough but I do think it will be tough escaping it...
Fair enough. I just thought this was worth suggesting.
Yes. I think you overlooked the comment in my example pointing out that for the Tuple version of the method you would have a temporary
Array
to work with. The nice thing about that is you get access to allArray
functions here without having to create Tuple equivalents.
Yea sorry I did not mean to overlook anything, I wanted to point out something that is probably implicit for us that is indeed not clear: having a temporary array is probably a no-go since the whole point of those methods is to avoid translating between arrays and tuples. I do agree this does simplify a lot of the API surface so the benefit/cost analysis is worth doing.
If you try to do
tuple.pushed(mutableThing)
you would get an error, too.
Wouldn't that be equivalent to.mutatedCopy()
producing an error?
I do agree with that point and sorry, again I have badly explained what I think is the issue: the location of the error. If you try to put an object in Tuple.prototype.pushed
, you will see the error where you called pushed
, whereas with mutatedCopy
you get it when you close the function block. While that is fine for userland features, I do think that the core language features should try to report errors from their exact originating site...
Fair enough. I just thought this was worth suggesting.
It definitely is worth suggesting and I'd like to play with the concept as both a userland feature and maybe a follow-on proposal. I just don't think it can replace those functions in both Arrays and Tuples unfortunately.
I'm going to close this issue. Though maybe someone will take this idea forward as it's own proposal in the future.
This proposal reduced it's scope down from adding 10 methods to 4, and we have settled on names (pending web compatibility analysis during stage 3 implementations).