Open rubenferreira97 opened 1 year ago
cc @lrhn
This is not a new idea. Heavens know I've had it many times myself. :grin:
The dragons are in the details. Starting with naming (which is probably also the easiest to fix).
Here you suggest that ImmutableX
is a supertype of X
, which is counter-intuitive naming. A mutable list is-an immutable list? It's clearly not!
final ImmutableList<int> l = [1, 2, 3];
(l as List<int>).add(4); // Gotcha!
That's because ImmutableList
is not the best name for it. It's a modifier (a negative one), which means it's read as a restriction/variant of something else (List
here), so it should really be a subtype of list
(it's a List
, but Immutable
). Which would mean it has to have the entire List
API anyway, and that's really where we are today.
It would be better to have List
be immutable and MutableList
be a mutable subtype. A positive modifier on a subclass, that's better style.
Or Vector
being immutable and List
a mutable subtype. Let's go with the last one (and ignoring that we'd also want a type for fixed-length lists).
abstract class Iterable<T> { ... }
abstract class Vector<T> implements Iterable<T> {
T operator[](int index);
// ...
}
abstract class List<T> implements Vector<T> {
operator[]=(int index, T value);
// ...
}
Then you can do:
void onlyReading(Vector<int> numbers) {
// random access reading numbers
}
void updateToo(List<int> numbers) {
int.add(42); // Because I can!
}
void main() {
List<int> ns = [1, 2, 3];
onlyReading(ns); // Valid, probably only reads.
updateToo(ns); // Valid, expect it to write.
}
Where this becomes an issue is that it means everybody has to decide, up front, whether to accept a List
or a Vector
.
If you only read values, you'd ask for a Vector
, but if you need to pass that vector to someone who expects a List
, you need to ask for a List
too. Changing a method from accepting Vector
to accepting List
is a breaking change (it changes a parameter to a subtype, that's always been breaking).
More crucial, any method returning a list need to decide whether it's a List
or a Vector
.
The safe bet is always returning a Vector
, and then you have to call toList
on it to get a list. (We can even make it a copy-on-write list if we are being really clever, since the original is immutable.)
That's what makes this change unlikely to happen. It will be incredibly hard to migrate all existing code to this behavior. Every method which goes from returning List
to returning Vector
is potentially breaking code which modifies the returned list. And you can't return that Vector
from a method which is still typed as returning List
.
We might need a completely new composite type system, like for null safety, where Vector
and List
are considered the same type in legacy code (all attempts to modify the Vector
will fail, just as for an immutable list today), but in new code, the List
is a subtype of Vector
, and Vector
doesn't have all the modifying members.
It's still a big migration. Code can't just change to returning immutable Vector
s where they used to return mutable lists. That's breaking too. And most code return mutable lists today.
Some functions simply cannot be migrated, like List<T> toList({bool growable = true})
, if we have a separate type for fixed-length lists too. Similarly, if any function today can return either a mutable list or an immutable one, depending on arguments, it cannot be typed correctly with the new types.
I'm not as opposed to the idea as I have been. It's clearly in the direction that Dart has already been moving for a while. But it's not trivial to get right, and it's a massive migration challenge.
You are right about the negative modifier being a problem. In my first approach I indeed started with this:
abstract class List<E> {...}
abstract class MutableList<E> implements List<E>, MutableCollection<E> {...}
But unfortunately, I knew that would be a huge breaking change on the current dart:core library API (List is immutable here, and nowdays List is mutable). I didn't find a better name like Vector
😁.
After some research I found that Kotlin follows this same approach:
interface List<out E> : Collection<E>
interface MutableList<E> : List<E>, MutableCollection<E>
Which would "solve" the first problem (the example intention is different though):
final MutableList<int> l = [1, 2, 3];
(l as List<int>).add(4); // safe cast MutableList<int> as List<int> but compile error, List does not implement add
final List<int> l2 = [1,2,3];
(l2 as MutableList<int>).add(4) // unsafe cast, fails at runtime
Kotlin also seems to have fun <T> Array<out T>.toMutableList(): [MutableList]
so it seems developers would usually return List
on their API and if you want to mutate, call this function. Your suggestion with copy-on-write is probably the same here (but an implementation detail).
So I think all of your suggestions are similar do Kotlin, and I gotta say it but it feels much better to work with collections in there than Dart unfortunately.
Personally, since the Dart 1 to Dart 2 shift, I feel many core elements/libraries feels like a patch and would need a rewrite to align with the new Dart 2 paradigm obtained via NNBD, a stronger type system, etc... The compromise I see here is: a) Keep the outdated approach, which is less safe and delegates many things to the runtime system (slower). In exchange, we don't have many breaking changes. b) Call some breaking changes, developers need to adapt, but in principle, get a language more aligned with the characteristics and features of Dart 2. I think we can't have both, it's up to the Dart team to decide ofc.
I don't like to feel that Dart 1 born crooked. The Dart team is taking a lot of effort (Great job BTW) on new features and I feel that the core API should not fall behind.
This is a surprising behavior for a lot of people. They make a const
collection of some sort and forget that it was const
, having a runtime error.
Most of the time they will notice before releasing to production, but it's still something that we could prevent statically.
Just yesterday, a user asked for help on Discord regarding this code:
/// Class to execute router updates in a centralized way.
///
/// Each sub-router must register a listener that updates them.
/// This is done by setting the [BeamerDelegate.updateListenable] parameter.
class SubRoutersUpdateListenable extends Listenable {
// const SubRoutersUpdateListenable();
SubRoutersUpdateListenable();
final _listeners = const <VoidCallback>{};
@override
void addListener(VoidCallback listener) => _listeners.add(listener);
@override
void removeListener(VoidCallback listener) => _listeners.remove(listener);
void execute() {
for (final listener in _listeners) {
listener();
}
}
}
He actually understood what was happening, but not why. Our discussion resulted more or less in what have been discussed here:
Mutable
variant of Set
;Either way, I think this breakage is valuable for a major version. We could obviously seek for a less-breaking migrating process, and dart fix
can help us (even though this migration is not as automatable as others).
I don't actually expect this change to happen. Not saying it cannot, but it's a breaking change on a scale that's very hard to argue will be worth the pain. Also, it's so much work that it's hard to argue that the opportunity cost is worth it, compared to other changes.
Let's imagine for a moment that we did want to make this change. We introduce two new types:
Vector<E> implements Iterable<E>
which has operator[]
, sublist
and efficient length, but no mutation at all. Constant lists have this type, as do Vector.of
(and List.unmodifiable
goes away, deferring to Vector.of
.)Array<E> implements Vector<E>
for fixed-length lists, which can change element values, but not use any function which changes length.List<E> implements Array<E>
is the current fully mutable list.Each will have the same general constructors as List
: .filled
, .generate
and .of
.
All {bool growable = ...}
parameters are removed from all methods and constructors that return lists. There is instead toList
, toArray
and toVector
methods on Iterable
. (We'll probably have to make toArray
and toVector
be extension methods, unless we can get interface default methods first.)
Typed Data lists will implement Array
, not List
.
That was the easy part. :sweat:
Then we go through every function that accepts or returns List
and decide which one it should return.
A very-quick-and-dirty grep
gives me the 326 places below. (I'll avoid putting text below it, no need to scroll!)
Most of the arguments, probably all, should just become Vector
.
Most of the returns should likely be Vector
too, because it's the simpler class, and you can do toList()
(we'll even ensure that it is fairly efficient) if you do need to make changes, but often you'll create a new instead, using list literals.
Exceptions are likely operations on List
, and the similar operations on Array
and Vector
, which should return the same type as the receiver (and accept Vector
s).
And every existing method with a {bool growable=}
would keep returning List
, now always growable, and deprecate the parameter.
Then all user code in existence will break. So we make a hack and let pre-feature code treat an occurrence of the type List
as a magical type which all of List
, Array
and Vector
are assignable to, and which has all the members of List
. If calling a non-Array
member on an object that is actually a Array
, it throws a runtime error.(Effectively all pre-feature List
-member invocations become partially dynamic invocations, but I expect we can implement this with suitable entries in the types' V-tables, so the performance should not be abysmal. Maybe the pre-feature "Leagcy-List
" type is really just the same as Vector
, with a static interface signature that has all the members of List
, and Vector
is implemented so that invoking members of List
on it will find a V-table entry that throws. The pre-feature code cannot see the Array
and List
subtypes, so it treates everything the same.)
When assigning a pre-feature List
to a post-feature List
or Array
, there will be an implicit dowcast which can fail at runtime.
It's like unsafe nullable types all over again: The two language versions do not agree on how many types there are, some of them are collapsed in the legacy code. In this case, it's not as ubiquitous as nullable types, there isn't a growable and non-growable version of every type, it's just one small, fixed type hierarchy. Which is used everywhere.
Migration will then likely be changing most List
types to Vector
, except the few cases where an argument list is modified, then it'll be either Array
or List
depending on whether you want to mutate it or not.
(Then we should consider doing the same thing for Map
and Set
, because List only really distinguishes itself by how often it's used. Any argument should apply to Map
and Set
too.)
The occurrences of List
in public member signatures:
sdk/lib/collection/collections.dart:31: List<R> cast<R>() => UnmodifiableListView(_source.cast<R>());
sdk/lib/collection/list.dart:223: List<E> toList({bool growable = true}) {
sdk/lib/collection/list.dart:310: List<R> cast<R>() => List.castFrom<E, R>(this);
sdk/lib/collection/list.dart:345: List<E> operator +(List<E> other) => [...this, ...other];
sdk/lib/collection/list.dart:347: List<E> sublist(int start, [int? end]) {
sdk/lib/collection/list.dart:555: static String listToString(List<Object?> list) =>
sdk/lib/collection/queue.dart:710: List<E> toList({bool growable = true}) {
sdk/lib/collection/set.dart:118: List<E> toList({bool growable = true}) =>
sdk/lib/convert/ascii.dart:51: String decode(List<int> bytes, {bool? allowInvalid}) {
sdk/lib/convert/ascii.dart:172: String convert(List<int> bytes, [int start = 0, int? end]) {
sdk/lib/convert/ascii.dart:264: void add(List<int> source) {
sdk/lib/convert/ascii.dart:268: void addSlice(List<int> source, int start, int end, bool isLast) {
sdk/lib/convert/ascii.dart:294: void add(List<int> source) {
sdk/lib/convert/ascii.dart:303: void addSlice(List<int> source, int start, int end, bool isLast) {
sdk/lib/convert/base64.dart:41:String base64Encode(List<int> bytes) => base64.encode(bytes);
sdk/lib/convert/base64.dart:46:String base64UrlEncode(List<int> bytes) => base64Url.encode(bytes);
sdk/lib/convert/base64.dart:240: String convert(List<int> input) {
sdk/lib/convert/base64.dart:312: Uint8List? encode(List<int> bytes, int start, int end, bool isLast) {
sdk/lib/convert/base64.dart:335: static int encodeChunk(String alphabet, List<int> bytes, int start, int end,
sdk/lib/convert/base64.dart:420: void add(List<int> source) {
sdk/lib/convert/base64.dart:428: void addSlice(List<int> source, int start, int end, bool isLast) {
sdk/lib/convert/byte_conversion.dart:28: void addSlice(List<int> chunk, int start, int end, bool isLast) {
sdk/lib/convert/byte_conversion.dart:48: void add(List<int> chunk) {
sdk/lib/convert/json.dart:442: List<int> convert(Object? object) {
sdk/lib/convert/json.dart:838: void writeList(List<Object?> list) {
sdk/lib/convert/json.dart:891: void writeList(List<Object?> list) {
sdk/lib/convert/latin1.dart:50: String decode(List<int> bytes, {bool? allowInvalid}) {
sdk/lib/convert/latin1.dart:146: void add(List<int> source) {
sdk/lib/convert/latin1.dart:159: void addSlice(List<int> source, int start, int end, bool isLast) {
sdk/lib/convert/latin1.dart:199: void addSlice(List<int> source, int start, int end, bool isLast) {
sdk/lib/convert/line_splitter.dart:52: List<String> convert(String data) {
sdk/lib/convert/string_conversion.dart:261: void add(List<int> chunk) {
sdk/lib/convert/string_conversion.dart:299: void add(List<int> chunk) {
sdk/lib/convert/string_conversion.dart:303: void addSlice(List<int> chunk, int startIndex, int endIndex, bool isLast) {
sdk/lib/convert/utf.dart:58: String decode(List<int> codeUnits, {bool? allowMalformed}) {
sdk/lib/convert/utf.dart:348: String convert(List<int> codeUnits, [int start = 0, int? end]) =>
sdk/lib/convert/utf.dart:547: external String convertSingle(List<int> codeUnits, int start, int? maybeEnd);
sdk/lib/convert/utf.dart:549: external String convertChunked(List<int> codeUnits, int start, int? maybeEnd);
sdk/lib/core/function.dart:144: external static apply(Function function, List<dynamic>? positionalArguments,
sdk/lib/core/invocation.dart:62: List<Type> get typeArguments => const <Type>[];
sdk/lib/core/invocation.dart:68: List<dynamic> get positionalArguments;
sdk/lib/core/iterable.dart:497: List<E> toList({bool growable = true}) =>
sdk/lib/core/list.dart:260: static List<T> castFrom<S, T>(List<S> source) => CastList<S, T>(source);
sdk/lib/core/list.dart:276: static void copyRange<T>(List<T> target, int at, List<T> source,
sdk/lib/core/list.dart:311: static void writeIterable<T>(List<T> target, int at, Iterable<T> source) {
sdk/lib/core/list.dart:342: List<R> cast<R>();
sdk/lib/core/list.dart:716: List<E> operator +(List<E> other);
sdk/lib/core/list.dart:739: List<E> sublist(int start, [int? end]);
sdk/lib/core/pattern.dart:130: List<String?> groups(List<int> groupIndices);
sdk/lib/core/string.dart:669: List<String> split(Pattern pattern);
sdk/lib/core/string.dart:700: List<int> get codeUnits;
sdk/lib/core/uri.dart:443: factory Uri.dataFromBytes(List<int> bytes,
sdk/lib/core/uri.dart:527: List<String> get pathSegments;
sdk/lib/core/uri.dart:3480: factory UriData.fromBytes(List<int> bytes,
sdk/lib/core/uri.dart:4571: List<String> get pathSegments {
sdk/lib/developer/http_profiling.dart:26:List<Map<String, dynamic>> getHttpClientProfilingData() {
sdk/lib/ffi/ffi.dart:213: const factory Array.variableMulti(List<int> dimensions) =
sdk/lib/html/dart2js/html_dart2js.dart:1328: List<BackgroundFetchSettledFetch>? get fetches native;
sdk/lib/html/dart2js/html_dart2js.dart:1443: List<BackgroundFetchSettledFetch>? get fetches native;
sdk/lib/html/dart2js/html_dart2js.dart:1559: List<String>? get platforms native;
sdk/lib/html/dart2js/html_dart2js.dart:1600: factory Blob(List blobParts, [String? type, String? endings]) {
sdk/lib/html/dart2js/html_dart2js.dart:1968: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:2059: List? get methodData native;
sdk/lib/html/dart2js/html_dart2js.dart:2061: List? get modifiers native;
sdk/lib/html/dart2js/html_dart2js.dart:2928: List<num> getLineDash() {
sdk/lib/html/dart2js/html_dart2js.dart:2943: void setLineDash(List<num> dash) {
sdk/lib/html/dart2js/html_dart2js.dart:3231: List<Node> getDistributedNodes() native;
sdk/lib/html/dart2js/html_dart2js.dart:3543: List<CssRule>? get cssRules native;
sdk/lib/html/dart2js/html_dart2js.dart:3613: List<CssRule>? get cssRules native;
sdk/lib/html/dart2js/html_dart2js.dart:8726: List<CssRule> get cssRules native;
sdk/lib/html/dart2js/html_dart2js.dart:8732: List<CssRule>? get rules native;
sdk/lib/html/dart2js/html_dart2js.dart:9119: List<Node>? get options native;
sdk/lib/html/dart2js/html_dart2js.dart:9147: List<File>? get files native;
sdk/lib/html/dart2js/html_dart2js.dart:9151: List<String>? get types native;
sdk/lib/html/dart2js/html_dart2js.dart:9428: List? get cornerPoints native;
sdk/lib/html/dart2js/html_dart2js.dart:9450: List? get landmarks native;
sdk/lib/html/dart2js/html_dart2js.dart:9470: List? get cornerPoints native;
sdk/lib/html/dart2js/html_dart2js.dart:10092: List<Animation> getAnimations() native;
sdk/lib/html/dart2js/html_dart2js.dart:10096: List<Node> getElementsByClassName(String classNames) native;
sdk/lib/html/dart2js/html_dart2js.dart:10100: List<Node> getElementsByName(String elementName) native;
sdk/lib/html/dart2js/html_dart2js.dart:10104: List<Node> getElementsByTagName(String localName) native;
sdk/lib/html/dart2js/html_dart2js.dart:10139: List<Element> elementsFromPoint(int x, int y) native;
sdk/lib/html/dart2js/html_dart2js.dart:10489: List<Element> get children {
sdk/lib/html/dart2js/html_dart2js.dart:10496: set children(List<Element> value) {
sdk/lib/html/dart2js/html_dart2js.dart:10614: List<StyleSheet>? get styleSheets native;
sdk/lib/html/dart2js/html_dart2js.dart:10618: List<Element> elementsFromPoint(int x, int y) native;
sdk/lib/html/dart2js/html_dart2js.dart:13046: set children(List<Element> value) {
sdk/lib/html/dart2js/html_dart2js.dart:13246: List<Rectangle> getClientRects() {
sdk/lib/html/dart2js/html_dart2js.dart:14730: List<Animation> getAnimations() native;
sdk/lib/html/dart2js/html_dart2js.dart:14738: List<String> getAttributeNames() native;
sdk/lib/html/dart2js/html_dart2js.dart:14785: List<Node> getDestinationInsertionPoints() native;
sdk/lib/html/dart2js/html_dart2js.dart:14798: List<Node> getElementsByClassName(String classNames) native;
sdk/lib/html/dart2js/html_dart2js.dart:15574: List<EventTarget> get path =>
sdk/lib/html/dart2js/html_dart2js.dart:15648: List<EventTarget> composedPath() native;
sdk/lib/html/dart2js/html_dart2js.dart:15919: List<MessagePort>? get ports native;
sdk/lib/html/dart2js/html_dart2js.dart:16054: List<Node>? get elements native;
sdk/lib/html/dart2js/html_dart2js.dart:16087: factory File(List<Object> fileBits, String fileName, [Map? options]) {
sdk/lib/html/dart2js/html_dart2js.dart:16637: List<FontFace>? get fontfaces native;
sdk/lib/html/dart2js/html_dart2js.dart:16712: List<Object> getAll(String name) native;
sdk/lib/html/dart2js/html_dart2js.dart:16825: List<num>? get axes native;
sdk/lib/html/dart2js/html_dart2js.dart:16829: List<GamepadButton>? get buttons native;
sdk/lib/html/dart2js/html_dart2js.dart:19162: List<File>? get files native;
sdk/lib/html/dart2js/html_dart2js.dart:19164: set files(List<File>? value) native;
sdk/lib/html/dart2js/html_dart2js.dart:19202: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:19303: List<Entry>? get entries native;
sdk/lib/html/dart2js/html_dart2js.dart:19363: List<Node>? get labels;
sdk/lib/html/dart2js/html_dart2js.dart:19790: List<File>? files;
sdk/lib/html/dart2js/html_dart2js.dart:19928: List<num>? get thresholds native;
sdk/lib/html/dart2js/html_dart2js.dart:19934: List<IntersectionObserverEntry> takeRecords() native;
sdk/lib/html/dart2js/html_dart2js.dart:20375: List<String>? get ancestorOrigins native;
sdk/lib/html/dart2js/html_dart2js.dart:20481: List<Node> get areas native;
sdk/lib/html/dart2js/html_dart2js.dart:21009: List? get artwork native;
sdk/lib/html/dart2js/html_dart2js.dart:21011: set artwork(List? value) native;
sdk/lib/html/dart2js/html_dart2js.dart:21274: List<MediaStreamTrack> getAudioTracks() native;
sdk/lib/html/dart2js/html_dart2js.dart:21278: List<MediaStreamTrack> getTracks() native;
sdk/lib/html/dart2js/html_dart2js.dart:21282: List<MediaStreamTrack> getVideoTracks() native;
sdk/lib/html/dart2js/html_dart2js.dart:21613: List<MessagePort> get ports native;
sdk/lib/html/dart2js/html_dart2js.dart:21795: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:22430:typedef void MutationCallback(List mutations, MutationObserver observer);
sdk/lib/html/dart2js/html_dart2js.dart:22496: List<MutationRecord> takeRecords() native;
sdk/lib/html/dart2js/html_dart2js.dart:22591: List<Node>? get addedNodes native;
sdk/lib/html/dart2js/html_dart2js.dart:22605: List<Node>? get removedNodes native;
sdk/lib/html/dart2js/html_dart2js.dart:22641: List<Gamepad?> getGamepads() {
sdk/lib/html/dart2js/html_dart2js.dart:22877: List<String>? get languages native;
sdk/lib/html/dart2js/html_dart2js.dart:22959: List<String>? get languages native;
sdk/lib/html/dart2js/html_dart2js.dart:23211: List<Node> get nodes {
sdk/lib/html/dart2js/html_dart2js.dart:23297: List<Node> get childNodes native;
sdk/lib/html/dart2js/html_dart2js.dart:23808: List? get actions native;
sdk/lib/html/dart2js/html_dart2js.dart:23842: List<int>? get vibrate native;
sdk/lib/html/dart2js/html_dart2js.dart:24243: List<num> getLineDash() native;
sdk/lib/html/dart2js/html_dart2js.dart:24290: void setLineDash(List<num> dash) native;
sdk/lib/html/dart2js/html_dart2js.dart:24441: List<num>? get quaternion native;
sdk/lib/html/dart2js/html_dart2js.dart:24481: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:24664: List<num> getLineDash() native;
sdk/lib/html/dart2js/html_dart2js.dart:24681: void setLineDash(List<num> dash) native;
sdk/lib/html/dart2js/html_dart2js.dart:24935: List<String>? get addressLine native;
sdk/lib/html/dart2js/html_dart2js.dart:25011: factory PaymentRequest(List<Map> methodData, Map details, [Map? options]) {
sdk/lib/html/dart2js/html_dart2js.dart:25079: List? get methodData native;
sdk/lib/html/dart2js/html_dart2js.dart:25081: List? get modifiers native;
sdk/lib/html/dart2js/html_dart2js.dart:25185: List<PerformanceEntry> getEntries() native;
sdk/lib/html/dart2js/html_dart2js.dart:25187: List<PerformanceEntry> getEntriesByName(String name, String? entryType)
sdk/lib/html/dart2js/html_dart2js.dart:25190: List<PerformanceEntry> getEntriesByType(String entryType) native;
sdk/lib/html/dart2js/html_dart2js.dart:25271: List<TaskAttributionTiming>? get attribution native;
sdk/lib/html/dart2js/html_dart2js.dart:25398: List<PerformanceEntry> getEntries() native;
sdk/lib/html/dart2js/html_dart2js.dart:25400: List<PerformanceEntry> getEntriesByName(String name, String? entryType)
sdk/lib/html/dart2js/html_dart2js.dart:25403: List<PerformanceEntry> getEntriesByType(String entryType) native;
sdk/lib/html/dart2js/html_dart2js.dart:25457: List<PerformanceServerTiming>? get serverTiming native;
sdk/lib/html/dart2js/html_dart2js.dart:25575: Future<PermissionStatus> requestAll(List<Map> permissions) =>
sdk/lib/html/dart2js/html_dart2js.dart:25596: List? get fillLightMode native;
sdk/lib/html/dart2js/html_dart2js.dart:25753: List<PointerEvent> getCoalescedEvents() native;
sdk/lib/html/dart2js/html_dart2js.dart:25986: List<PresentationConnection>? get connections native;
sdk/lib/html/dart2js/html_dart2js.dart:26087: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:26417: List<Rectangle> getClientRects() {
sdk/lib/html/dart2js/html_dart2js.dart:26589:typedef void ResizeObserverCallback(List entries, ResizeObserver observer);
sdk/lib/html/dart2js/html_dart2js.dart:26618: List<Map> getFingerprints() native;
sdk/lib/html/dart2js/html_dart2js.dart:26855: List<String> names() native;
sdk/lib/html/dart2js/html_dart2js.dart:27087: List<MediaStream> getLocalStreams() native;
sdk/lib/html/dart2js/html_dart2js.dart:27089: List<RtcRtpReceiver> getReceivers() native;
sdk/lib/html/dart2js/html_dart2js.dart:27091: List<MediaStream> getRemoteStreams() native;
sdk/lib/html/dart2js/html_dart2js.dart:27093: List<RtcRtpSender> getSenders() native;
sdk/lib/html/dart2js/html_dart2js.dart:27213: List<RtcRtpContributingSource> getContributingSources() native;
sdk/lib/html/dart2js/html_dart2js.dart:27334: List<RtcLegacyStatsReport> result() native;
sdk/lib/html/dart2js/html_dart2js.dart:27356: List<MediaStream>? get streams native;
sdk/lib/html/dart2js/html_dart2js.dart:27655: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:27709: List<OptionElement> get options {
sdk/lib/html/dart2js/html_dart2js.dart:27714: List<OptionElement> get selectedOptions {
sdk/lib/html/dart2js/html_dart2js.dart:28042: List<Node> getDistributedNodes() native;
sdk/lib/html/dart2js/html_dart2js.dart:28082: List<StyleSheet>? get styleSheets native;
sdk/lib/html/dart2js/html_dart2js.dart:28086: List<Element> elementsFromPoint(int x, int y) native;
sdk/lib/html/dart2js/html_dart2js.dart:28266: List<Node> assignedNodes([Map? options]) {
sdk/lib/html/dart2js/html_dart2js.dart:28823: List<SpeechRecognitionResult>? get results native;
sdk/lib/html/dart2js/html_dart2js.dart:28849: List<SpeechSynthesisVoice> getVoices() {
sdk/lib/html/dart2js/html_dart2js.dart:29355: List<CssStyleValue> getAll(String property) native;
sdk/lib/html/dart2js/html_dart2js.dart:29357: List<String> getProperties() native;
sdk/lib/html/dart2js/html_dart2js.dart:29521: List<TableSectionElement> get tBodies =>
sdk/lib/html/dart2js/html_dart2js.dart:29629: List<TableCellElement> get cells =>
sdk/lib/html/dart2js/html_dart2js.dart:29831: List<Node> getDestinationInsertionPoints() native;
sdk/lib/html/dart2js/html_dart2js.dart:29887: List<Node>? get labels native;
sdk/lib/html/dart2js/html_dart2js.dart:31033: List<String> getAll(String name) native;
sdk/lib/html/dart2js/html_dart2js.dart:31183: List<Map> getLayers() native;
sdk/lib/html/dart2js/html_dart2js.dart:31187: Future requestPresent(List<Map> layers) =>
sdk/lib/html/dart2js/html_dart2js.dart:31394: List<VRStageBoundsPoint>? get geometry native;
sdk/lib/html/dart2js/html_dart2js.dart:33028: List<CssRule> getMatchedCssRules(Element? element, String? pseudoElement)
sdk/lib/html/dart2js/html_dart2js.dart:34055: List<PerformanceEntry> getEntries() native;
sdk/lib/html/dart2js/html_dart2js.dart:34057: List<PerformanceEntry> getEntriesByName(String name, String? entryType)
sdk/lib/html/dart2js/html_dart2js.dart:34060: List<PerformanceEntry> getEntriesByType(String entryType) native;
sdk/lib/html/dart2js/html_dart2js.dart:39860: FixedSizeListIterator(List<T> array)
sdk/lib/html/dart2js/html_dart2js.dart:39970: void table([Object? tabularData, List<String>? properties]) =>
sdk/lib/html/dart2js/html_dart2js.dart:40591: List<EventTarget> composedPath() => wrapped.composedPath();
sdk/lib/html/dart2js/html_dart2js.dart:40620: List<Node> get path => wrapped.path as List<Node>;
sdk/lib/html/html_common/conversions.dart:83: List newJsList(length);
sdk/lib/html/html_common/conversions.dart:156: List copyList(List e, int slot) {
sdk/lib/html/html_common/conversions.dart:221: List newDartList(length);
sdk/lib/html/html_common/conversions_dart2js.dart:78:List convertDartToNative_StringArray(List<String> input) {
sdk/lib/html/html_common/conversions_dart2js.dart:123: List newJsList(length) => JS('JSExtendableArray', 'new Array(#)', length);
sdk/lib/html/html_common/conversions_dart2js.dart:124: List newDartList(length) => newJsList(length);
sdk/lib/html/html_common/css_class_set.dart:190: List<String> toList({bool growable = true}) =>
sdk/lib/html/html_common/lists.dart:13: static int indexOf(List a, Object element, int startIndex, int endIndex) {
sdk/lib/html/html_common/lists.dart:33: static int lastIndexOf(List a, Object element, int startIndex) {
sdk/lib/html/html_common/lists.dart:55: static List getRange(List a, int start, int end, List accumulator) {
sdk/lib/html/html_common/lists.dart:70: List<Node> get rawList;
sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart:337: Transaction transactionList(List<String> storeNames, String mode) {
sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart:400: List<String>? get objectStoreNames native;
sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart:875: List<String>? get indexNames native;
sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart:1232: List<String>? get objectStoreNames native;
sdk/lib/internal/bytes_builder.dart:37: void add(List<int> bytes);
sdk/lib/internal/bytes_builder.dart:89: void add(List<int> bytes) {
sdk/lib/internal/bytes_builder.dart:181: void add(List<int> bytes) {
sdk/lib/internal/cast.dart:176: List<R> cast<R>() => new CastList<S, R>(_source);
sdk/lib/internal/iterable.dart:223: List<E> toList({bool growable = true}) =>
sdk/lib/internal/iterable.dart:314: List<E> toList({bool growable = true}) {
sdk/lib/internal/iterable.dart:782: List<E> toList({bool growable = true}) => List<E>.empty(growable: growable);
sdk/lib/internal/sort.dart:32: static void sort<E>(List<E> a, int compare(E a, E b)) {
sdk/lib/internal/sort.dart:45: static void sortRange<E>(List<E> a, int from, int to, int compare(E a, E b)) {
sdk/lib/io/common.dart:115: List<int> buffer;
sdk/lib/io/data_transformer.dart:304: List<int> convert(List<int> bytes) {
sdk/lib/io/data_transformer.dart:362: List<int> convert(List<int> bytes) {
sdk/lib/io/data_transformer.dart:414: void process(List<int> data, int start, int end);
sdk/lib/io/data_transformer.dart:425: List<int>? processed({bool flush = true, bool end = false});
sdk/lib/io/data_transformer.dart:443: void add(List<int> chunk) {
sdk/lib/io/data_transformer.dart:447: void addSlice(List<int> chunk, int start, int end, bool isLast) {
sdk/lib/io/data_transformer.dart:493: void add(List<int> data) {
sdk/lib/io/data_transformer.dart:497: void addSlice(List<int> data, int start, int end, bool isLast) {
sdk/lib/io/directory.dart:372: List<FileSystemEntity> listSync(
sdk/lib/io/directory_impl.dart:222: List<FileSystemEntity> listSync(
sdk/lib/io/directory_impl.dart:397: void error(List<Object?> message) {
sdk/lib/io/file.dart:613: List<String> readAsLinesSync({Encoding encoding = utf8});
sdk/lib/io/file.dart:632: Future<File> writeAsBytes(List<int> bytes,
sdk/lib/io/file.dart:652: void writeAsBytesSync(List<int> bytes,
sdk/lib/io/file.dart:778: Future<int> readInto(List<int> buffer, [int start = 0, int? end]);
sdk/lib/io/file.dart:793: int readIntoSync(List<int> buffer, [int start = 0, int? end]);
sdk/lib/io/file.dart:818: Future<RandomAccessFile> writeFrom(List<int> buffer,
sdk/lib/io/file.dart:830: void writeFromSync(List<int> buffer, [int start = 0, int? end]);
sdk/lib/io/file_impl.dart:629: List<String> readAsLinesSync({Encoding encoding = utf8}) =>
sdk/lib/io/file_impl.dart:632: Future<File> writeAsBytes(List<int> bytes,
sdk/lib/io/file_impl.dart:642: void writeAsBytesSync(List<int> bytes,
sdk/lib/io/file_impl.dart:694: readInto(List<int> buffer, int start, int? end);
sdk/lib/io/file_impl.dart:696: writeFrom(List<int> buffer, int start, int? end);
sdk/lib/io/file_impl.dart:806: Future<int> readInto(List<int> buffer, [int start = 0, int? end]) {
sdk/lib/io/file_impl.dart:825: int readIntoSync(List<int> buffer, [int start = 0, int? end]) {
sdk/lib/io/file_impl.dart:863: Future<RandomAccessFile> writeFrom(List<int> buffer,
sdk/lib/io/file_impl.dart:891: void writeFromSync(List<int> buffer, [int start = 0, int? end]) {
sdk/lib/io/io_sink.dart:58: void add(List<int> data);
sdk/lib/io/secure_socket.dart:796: int write(List<int> data, [int offset = 0, int? bytes]) {
sdk/lib/io/secure_socket.dart:823: int sendMessage(List<SocketControlMessage> controlMessages, List<int> data,
sdk/lib/io/secure_socket.dart:1222: List<int>? data;
sdk/lib/io/secure_socket.dart:1290: int write(List<int> inputData, int offset, int bytes) {
sdk/lib/io/secure_socket.dart:1307: int writeFromSource(List<int>? getData(int requested)) {
sdk/lib/io/secure_socket.dart:1365: List<_ExternalBuffer>? get buffers;
sdk/lib/io/security_context.dart:96: void usePrivateKeyBytes(List<int> keyBytes, {String? password});
sdk/lib/io/security_context.dart:123: void setTrustedCertificatesBytes(List<int> certBytes, {String? password});
sdk/lib/io/security_context.dart:147: void useCertificateChainBytes(List<int> chainBytes, {String? password});
sdk/lib/io/security_context.dart:170: void setClientAuthoritiesBytes(List<int> authCertBytes, {String? password});
sdk/lib/io/security_context.dart:187: void setAlpnProtocols(List<String> protocols, bool isServer);
sdk/lib/io/socket.dart:177: List<InternetAddress> get addresses;
sdk/lib/io/socket.dart:674: int write(List<int> buffer, [int offset = 0, int? count]);
sdk/lib/io/socket.dart:704: int sendMessage(List<SocketControlMessage> controlMessages, List<int> data,
sdk/lib/io/socket.dart:1040: List<ResourceHandle> extractHandles();
sdk/lib/io/socket.dart:1234: int send(List<int> buffer, InternetAddress address, int port);
sdk/lib/io/stdio.dart:448: void add(List<int> data) {
sdk/lib/io/string_transformer.dart:26: List<int> encode(String input) => encoder.convert(input);
sdk/lib/io/string_transformer.dart:27: String decode(List<int> encoded) => decoder.convert(encoded);
sdk/lib/io/string_transformer.dart:49: List<int> convert(String input) {
sdk/lib/io/string_transformer.dart:97: String convert(List<int> input) {
sdk/lib/io/string_transformer.dart:121: void add(List<int> bytes) {
sdk/lib/io/sync_socket.dart:48: int readIntoSync(List<int> buffer, [int start = 0, int? end]);
sdk/lib/io/sync_socket.dart:56: List<int>? readSync(int bytes);
sdk/lib/io/sync_socket.dart:73: void writeFromSync(List<int> buffer, [int start = 0, int? end]);
sdk/lib/isolate/isolate.dart:1050: external factory TransferableTypedData.fromList(List<TypedData> list);
sdk/lib/js/js.dart:175: external dynamic apply(List args, {thisArg});
sdk/lib/js_util/js_util.dart:54:external T callMethod<T>(Object o, Object method, List<Object?> args);
sdk/lib/js_util/js_util.dart:62:external T callConstructor<T>(Object constr, List<Object?>? arguments);
sdk/lib/mirrors/mirrors.dart:373: List<InstanceMirror> get metadata;
sdk/lib/mirrors/mirrors.dart:410: InstanceMirror invoke(Symbol memberName, List<dynamic> positionalArguments,
sdk/lib/mirrors/mirrors.dart:601: InstanceMirror apply(List<dynamic> positionalArguments,
sdk/lib/mirrors/mirrors.dart:639: List<LibraryDependencyMirror> get libraryDependencies;
sdk/lib/mirrors/mirrors.dart:667: List<CombinatorMirror> get combinators;
sdk/lib/mirrors/mirrors.dart:672: List<InstanceMirror> get metadata;
sdk/lib/mirrors/mirrors.dart:683: List<Symbol> get identifiers;
sdk/lib/mirrors/mirrors.dart:720: List<TypeVariableMirror> get typeVariables;
sdk/lib/mirrors/mirrors.dart:735: List<TypeMirror> get typeArguments;
sdk/lib/mirrors/mirrors.dart:790: List<ClassMirror> get superinterfaces;
sdk/lib/mirrors/mirrors.dart:916: List<ParameterMirror> get parameters;
sdk/lib/mirrors/mirrors.dart:988: List<ParameterMirror> get parameters;
sdk/lib/svg/dart2js/svg_dart2js.dart:3021: List<Element> get children => new FilteredElementList(this);
sdk/lib/svg/dart2js/svg_dart2js.dart:3023: set children(List<Element> value) {
sdk/lib/svg/dart2js/svg_dart2js.dart:3518: List<Node> getEnclosureList(Rect rect, SvgElement? referenceElement) native;
sdk/lib/svg/dart2js/svg_dart2js.dart:3522: List<Node> getIntersectionList(Rect rect, SvgElement? referenceElement)
sdk/lib/typed_data/typed_data.dart:372: List<int> operator +(List<int> other);
sdk/lib/typed_data/typed_data.dart:382: List<double> operator +(List<double> other);
sdk/lib/typed_data/typed_data.dart:753: external factory Int8List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:870: external factory Uint8List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:940: List<int> operator +(List<int> other);
sdk/lib/typed_data/typed_data.dart:994: external factory Uint8ClampedList.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1115: external factory Int16List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1244: external factory Uint16List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1374: external factory Int32List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1503: external factory Uint32List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1633: external factory Int64List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1762: external factory Uint64List.fromList(List<int> elements);
sdk/lib/typed_data/typed_data.dart:1893: external factory Float32List.fromList(List<double> elements);
sdk/lib/typed_data/typed_data.dart:2016: external factory Float64List.fromList(List<double> elements);
sdk/lib/typed_data/typed_data.dart:2139: external factory Float32x4List.fromList(List<Float32x4> elements);
sdk/lib/typed_data/typed_data.dart:2218: List<Float32x4> operator +(List<Float32x4> other);
sdk/lib/typed_data/typed_data.dart:2270: external factory Int32x4List.fromList(List<Int32x4> elements);
sdk/lib/typed_data/typed_data.dart:2349: List<Int32x4> operator +(List<Int32x4> other);
sdk/lib/typed_data/typed_data.dart:2405: external factory Float64x2List.fromList(List<Float64x2> elements);
sdk/lib/typed_data/typed_data.dart:2411: List<Float64x2> operator +(List<Float64x2> other);
sdk/lib/vmservice/message.dart:256:external bool sendIsolateServiceMessage(SendPort sp, List<Object?> m);
sdk/lib/vmservice/message.dart:259:external void sendRootServiceMessage(List<Object?> m);
sdk/lib/vmservice/message.dart:262:external void sendObjectRootServiceMessage(List<Object?> m);
sdk/lib/vmservice/vmservice.dart:145:typedef Future<void> WriteFileCallback(Uri path, List<int> bytes);
sdk/lib/web_audio/dart2js/web_audio_dart2js.dart:437: AudioParam setValueCurveAtTime(List<num> values, num time, num duration)
sdk/lib/web_audio/dart2js/web_audio_dart2js.dart:706: IirFilterNode createIirFilter(List<num> feedForward, List<num> feedBack)
sdk/lib/web_audio/dart2js/web_audio_dart2js.dart:721: PeriodicWave createPeriodicWave(List<num> real, List<num> imag,
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:373: void drawBuffersWebgl(List<int> buffers) native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:859: List<Shader>? getAttachedShaders(Program program) native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:912: List<String>? getSupportedExtensions() native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:1493: void drawBuffers(List<int> buffers) native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:1541: List<int>? getUniformIndices(Program program, List<String> uniformNames) {
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:1549: void invalidateFramebuffer(int target, List<int> attachments) native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:1551: void invalidateSubFramebuffer(int target, List<int> attachments, int x, int y,
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:2436: List<Shader>? getAttachedShaders(Program program) native;
sdk/lib/web_gl/dart2js/web_gl_dart2js.dart:2473: List<String>? getSupportedExtensions() native;
Coding a little in dart I have found some challenges utilizing the dart:core collection library.
I would like to propose a way to express imutable and mutable collections:
This would solve the following problem (and many others) that I have faced:
Edit: Changed naming convention (to avoid negative modifiers)