I don't understand why the authors of Dart defined fields of record instance as 'final'.
I think, that Record concept is convenient shortcut to create typical class. But fields in class don't have to final, so are editable after creating of instance.
To edit record.field
, if you type:
({int x, String y}) record= (x: 1,y: 'cat');
...
record.x= 3; // **not possible :(**
You must write e.g.:
void _record({int? a, String? b}){
record= (x: a ?? record.x, y: b ?? record.y);
}
It's strange and disqualifies using Record more broadly, doesn't it?
I don't understand why the authors of Dart defined fields of record instance as 'final'.
I think, that Record concept is convenient shortcut to create typical class. But fields in class don't have to final, so are editable after creating of instance.
To edit record.field
, if you type:
({int x, String y}) record= (x: 1,y: 'cat');
...
record.x= 3; // **not possible :(**
You must write e.g.:
void _record({int? a, String? b}){
record= (x: a ?? record.x, y: b ?? record.y);
}
It's strange and disqualifies using Record more broadly, doesn't it?
Share Improve this question edited Jan 31 at 11:44 jonrsharpe 122k30 gold badges268 silver badges476 bronze badges asked Jan 31 at 11:02 VoldemortVoldemort 1 2- 1 I'm not a Dart programmer, but this idea of having immutable data carriers like this is actually quite a common one in different languages. It might surprise you to know that it's possible to build big systems like this. See, for example youtube/watch?v=APUCMSPiNh4 or youtube/watch?v=APUCMSPiNh4 for reasons why you'd want to do this. – ndc85430 Commented Feb 1 at 17:51
- I unfortunately posted the same link twice. The other one I should have posted is youtu.be/8Sf6ToPNiA4 – ndc85430 Commented Feb 2 at 10:01
2 Answers
Reset to default 4One goal for records was to allow an optimizing compiler to not allocate an object for them.
If the compiler can see that you're just passing around records with the same structure, like a pair, (int, String)
or (bool, Object)
, then it can choose to instead pass around two individual values, and int
and a String
, without ever allocating a record object on the heap.
To allow that, records must not have identity. You must not be able to see that the compiler threw away a record, worked a while on the values, and then created a new record with the same values.
If an object is mutable, it neecssarily has identity. You're mutating that object, not any other equivalent object with the same state.
If you want mutability, you need identitiy, and if you need identity, you should use a class. Object oriented programming has identity of objects as one of its principal ideas. Records, in the Dart sense, are intended for a more value based programming style.
Another reason is that records are structurally typed.
If you have a pair of (2, 2)
, it has type (int, int)
. Even if you start out creating it as (num, num) pair = (2, 2);
, it won't inherently be a (num, num)
pair. YOu can do pair as (int, int)
and it succeeds.
That also doesn't work with mutability, even if you had identity.
(num, num) numPair = (2, 2);
(int, int) intPair = numPair as (int, int); // Succeeds.
numPair.$1 = 3.14; // If you could change values.
int x = intPair.$1; // Whoops.
Dart generics retain the type arguments at runtime, which is why a <num>[1, 2]
list is not a List<int>
. Structurally typed values do not retain any type variables at runtime, so they can't prevent you from up-casting and then mutating, if you can mutate. Unless you prevent up-casting, which is even more invasive.
Records are not meant as a short-cut to create a class. Instead, records are an
"anonymous, immutable, aggregate type"
that are
"structurally typed based on the types of their fields".
Their main use is to bundle multiple values of different types together, for example, to return multiple values from a function (in a type-safe manner), or in the context of pattern-matching.