-
Notifications
You must be signed in to change notification settings - Fork 213
Protected extension types or proper "Value types" #1467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
If we ignore the syntax differences, the significant difference is that you declare the variable in the class part (outside the body), I declare it in the body and reference it in the class part: // No need to write `wrapper`, the forwarding should work for all classes.
class Bar on Foo _foo, Baz _baz implements Qux {
...
} vs. class Bar implements Foo as _foo, Baz as _baz, Qux {
Foo _foo;
Baz _baz;
...
} Yours is definitely shorter in some cases, but (IMO) harder to read. I can't just look at the top of the object declaration and see the state. class Foo implements Bar as _b, Baz as _b, Qux {
BarBaz _b;
...
} My approach doesn't allow exposing members which are not part of an interface. There is no Bar bar show bar1, bar2; would introduce forwarders for As for auto-unboxing, that's a separate feature. I wouldn't call it |
The idea is that, as an alternative to "protected extension types", we should do "value types" (which can be unboxed and boxed as needed, and do not promise identity) and "easy forwarding" (for the |
One problem with this idea is that value types are traditionally unmodifiable and not subtypable. If something does not have identity, mutating it makes little sense, and if you can replace it with something which has more state prevents unboxing. For the non-subclassability, that probably means that you cannot extend (or implement) a For unmodifiability, either we can just go with that ( Example: Maybe just define If we introduce a supertype of all values, |
Stack allocation is a reason for value types, but it's only part of it. (Not just stack allocation, also inline-allocation in other objects, so let's call it non-heap allocation, where the "heap" here is the garbage collected object heap, not a C-like heap). The reason for non-heap allocated objects is memory use reduction, memory churn reduction and therefore lower garbage collection overhead. It's space, and thereby time, optimization, not a semantic change in how the object works—at least not unless we need to add further restrictions. So why do we talk about it at all? Because we actually do care about the overhead of allocating new objects. Maybe we shouldn't, and we should just optimize the normal objects better. The real goal here is good and predictable performance, because without that, Dart won't be a good platform for performance sensitive programs. The cost of removing identity and allowing inline allocation is that you can't safely store a reference to a non-heap allocated object because either it might stop existing at any time (if it's on the stack) or it doesn't exist as an independent entity (if inlined in another object). |
I like value types with syntax of struct Bar from Foo as _foo, Baz as _baz, Qux {
Foo _foo;
Baz _baz;
...
} without auto-forwarding by default, |
I agree that we'll need something, hopefully not pointers. It's not a given that a "value type" is mutable. It such an overloaded word, used to mean both "data type" (immutable values) and "structs" (potentially mutable like objects, but without a permanent identity), or things in-between. You're talking about the struct. Reference variables would work, something like There is one other approach we can take, one which doesn't require pointers: Functional Updates!
Basically syntax like You can have nested structs and do It would be up to the compiler to optimize such assignments into in-place updates, or combine things It's not quite mutable structs, but it simulates it adequately (there are things about evaluation order too, for cases like That's the one non-reference/non-pointer approach I've seen to updating structs, no pointer exposed, no reference variables needed, but it does rely on the struct variable being assignable. |
All object references are reference/pointer types behind the scenes. We're hiding them fairly successfully now (I hope 😓). Struct references are no different in that regard, the question is whether you can share a reference, which we always do with objects. Admit I can't think of any language with mutable non-object structures and no pointers/reference variables. Even C# has |
Any updates on this? Forgive me if im saying nonsense, but im wondering what if we had structs from day one before flutter even started. Considering SwiftUI (flutter style for building UIs) uses structs, maybe flutter could have worked with such instead of relying on stateful x stateless widgets classes. Just a thought, anyone more knowledgeable than me to have some good insights on the subject? |
Whats the team's thoughts on javascript potentially adding them? https://github.com/tc39/proposal-structs |
JavaScript structs are just fixed layout objects. They are not value types. Dart already has this - any class instance is fixed layout. |
The current specification of extension types suggests
protected extension type
declarations to introduce a type which:on
type (only toObject?
andNever
),Object?
).It's not a real wrapper object, although there is an extra part of the proposal to introduce
box
/unbox
getters to do explicit boxing and unboxing.To me, this all sounds very much like an attempt to define a value type, meaning a type with no (guaranteed) identity for values, allowing automatic boxing and unboxing as necessary, but otherwise looking like a class. The
show
/hide
clauses allow us to forward some methods to the underlying value, which is a feature we could use in other situations too.So, if we instead allowed a declaration like:
The
struct
works the same asclass
, but introduces a value without identity. This allows the compiler to inline/unbox and re-box the object as it pleases. I'd probably wantidentical
to either always answerfalse
, or always doidentical
comparisons on the field values. Structs do not introduce an interface and cannot be subclassed, or something like that (lots of possible details).The
implements type as field
automatically adds forwarders for all non-overridden members oftype
tofield.member
. (We can perhaps also allowshow/hide
after theas field
, but it's a problem thatshow
/hide
uses a comma separated list, just likeimplements
).There are also other options for forwarding, but this can handle bulk forwarding.
Together, these two features would give us all the affordability of protected extension types, except guaranteed zero overhead. It's a compiler optimization to inline structs where possible, but something that should be fairly predictable (whenever the static type is the value type, we can safely unbox it).
In return, we get general value type which can contain more than one value (extension types are only on one value), which work safely with generic containers (by auto-boxing), and where boxing/autoboxing is built into the system, not something you have to do manually using
box
/unbox
. This is safer than the skin-deep abstraction of an extension type can ever be.The forwarding feature can also be used by classes, and for more than one type at a time. It's a request we've had before (like #3748).
The text was updated successfully, but these errors were encountered: