Open matanlurey opened 11 months ago
Ad 1:
extension type Level._(int value) {
Level(this.value) {
RangeError.checkValueInInterval(value, 1, 20);
}
}
You need the "primary constructor" declaration as part of the type declaration, but you can make it private and provide another public constructor instead.
As you say, you can't prevent someone from writing -1 as Level
.
Ad 2: Yes.
All the "members of Object
" are forwarded directly to the representation type.
You can't redeclare them, so they always work as the representation object.
Ad 3. You can implement any interface type that's a supertype of the representation type. The extension type does not implement an interface implicitly, you have to ask for it.
In this case, you can't implement
Comparable<Level>
because that's not a supertype of the interface implemented by the representation type,int
implementsComparable<num>
, and even ifLevel
had implementedint
,Comparable<Level>
is a subtype, not a supertype, ofComparable<num>
. (If we had variance annotations,Comparable
would be contravariant, and then it would have worked. But we don't.) As it is,Level
is unrelated toint
andnum
.
Thanks for the context (and @mraleph for engaging on the bird app).
I believe as written, I won't be able to utilize extension types much, as they seem only suitable for providing methods on variants of objects - potentially super useful for stuff like FFI or JS Interop, but less useful for domain objects. I do wonder what that means for something like:
assert(foo is Pointer); // will this basically always be true? will lints/diagnostics try to catch these problems?
If we change Pointer
to be an extension type wrapping an int
, then 2 is Pointer
will be true. Because it is a pointer, if you want it to be. You can write Pointer<Void>.fromAddress(2)
today, and you can write the same for the extension type. And you can then also do 2 as Pointer<Void>
in good C-style.
There are many use-cases that extension types won't be good for. They do not intend to replace classes. Most likely, almost all of your types will keep being classes, because classes provide virtual methods, encapsulation, abstraction, and retained type parameters. All the reasons you use object oriented programming today, instead of just passing data around and calling static functions in good imperative style.
Extension types are primarily aimed at things like integration and interoperability, where the representation value is an artificial Dart container for the real non-Dart data, which has a Dart API which doesn't reflect the API that you want. Like an FFI struct on top of a pointer or typed data buffer. Those could also be Dart wrapper classes, but the allocation overhead of repeatedly boxing and unboxing for a large API is measurable (fx in JS integration or protobuf). Extension types are intended to address that problem.
Now, if you find other uses for it, that's fine, even expected, but those uses will come with inherent limitations, which a class based solution wouldn't have.
For example, extension types do not attempt to allow you to define subset types, like EvenInt
which only allows the even integers. You can try, by only providing a public constructor that rejects odd integers, but you can't prevent 3 as EvenInt
. That's just not a supported use-case for extension types, whereas a class would be able to trust that creation goes through a constructor.
Hi folks! Really excited, but I admit I have a hard time reading the specification and couldn't find good examples to refer to.
Feel free to close if these are all handled, or shouldn't be handled (though I suspect they should). Most of these questions come from wanting to avoid so-called "primitive obsession" in our code, i.e. over-using primitives such as
int
for dubious reasons (convenience, terseness, performance):Can the "constructor" have logic similar to below?
I understand it will be possible to do so-called "unsafe" casts (possibly catchable with a lint), but for the "normal" use case, can I require validation before (explicit) conversion? For example, can I limit the
Level
type to integer values of 1 through 20?Is
==
andhashCode
forwarded for me? If so, how about these cases?In short, if
IdNumber(1) == 1
, I'm going to be sad.Is my/can my extension type be, say,
Comparable
?Is it comparable by default because
int
is?What if I'm wrapping a type that isn't comparable, such as
Uri
?I'm worried that without support for these 3 scenarios, it's still going to be attractive to either continue using primitives, or extension types will be used, but they'll lack a lot of the flair of the pattern in say, Rust's newtype idiom.
Thanks for listening!