-
Notifications
You must be signed in to change notification settings - Fork 214
Enum bound inheritance #2548
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
Can you elaborate a bit on how this is different than sealed classes + patterns? You could write: /// Sealed classes can only be extended in their own library,
/// so the only subclasses are [Bird], [Land], and [Sea].
sealed abstract class Animal { }
class Bird extends Animal {
void fly() { }
}
class Land extends Animal {
int? legs;
void walk() { }
}
class Sea extends Animal {
bool isMammal;
void swim() { }
}
void doSomething(Animal animal) {
// Since Dart knows all possible subclasses of [animal], it knows this is exhaustive
switch(animal) {
case Land: animal.walk(); break;
case Sea: animal.swim(); break;
case Bird: animal.fly(); break;
}
} |
Thank you for the response @Levi-Lesches ! I was mistaken in my understanding of sealed classes. You're right, what I'm suggesting is only sealed classes with twisted syntax. btw, are sealed classes still on the dart team agenda? It's not clear there where the discussion had settled eventually. |
Note that the notion of 'sealed' classes hasn't been fully clarified. Mainly, there are two different proposals: One rule is that every non-bottom subtype of a sealed class The other rule is that every non-bottom direct subtype of // Library 'lib.dart'.
sealed abstract class C {}
class D1 extends C {}
class D2 implements C {}
class D3 extends Object with C {}
// Library 'lib2.dart'.
import 'lib.dart';
class D1a extends D1 {} // Error with rule 1, allowed with rule 2.
class D1b extends D1 {} // Ditto.
// With rule 1, assume that D1a, D1b do not exist.
void main() {
C c =...;
// With rule 1, this switch handles every concrete run-time type explicitly.
// With rule 2, it handles every possible type, but the compiler
// won't tell us that `case D1` could also be a `D1a` or a `D1b`.
var x = switch (c) {
case D1 _ => 1;
case D2 _ => 2;
case D3 _ => 3;
};
// Anyway, we could as well have a switch that covers all possible
// types without matching on the most special type on each branch.
var y = switch (c) {
case D2 _ => '2';
case C => 'not 2'; // Includes `D1` and `D3`, so we're done.
};
} |
Yes, we're still working on it and the current plan is to make it part of the patterns feature. I'm going to close this issue because I think sealed types do what you want. |
@eernstg Thank you so much for taking the time to write this clarification! From my humble understanding, the tradeoff is in performance and usability. Performance-wise, rule-1 is obviously better as methods resolving and inlining are easier. Let's assume that instead of using the standard So P.s. By using words like "list", "property" and "register", I don't mean them in the way we use them in programming. I'm aware that, if my suggestion makes any sense, these concepts need to be implemented on a language level in a much different way using formal linguistics notions far beyond my comprehension. I'm only using the analogy of the observer pattern to deliver my idea. I'd appreciate your guidance on where I can start fairly light research on this subject (a link or a term to look for). In case what I'm suggesting is fundamentally wrong and there's no single mistake you can point out, you can tell me so by a dislike on this comment 😄. |
No dislikes here! ;-) Maybe you could think about Traditionally, the approach used in a switch statement has been associated with algebraic datatypes (as in many functional languages). They trivially support exhaustiveness checks because each algebraic datatype has a locally known set of forms. This tension has been studied for many years, and the phrase 'the expression problem' is a very good starting point. |
The feature basically allows us to create a class, that must, and can only be extended by certain classes. These certain classes are defined by an enum.
I'll introduce two new keywords in the following examples
boundTo
andtypeOf
.I don't mean that the suggested features will necessarily require new keywords, on the contrary, I'd like to see it implemented in a simpler way. The two new keywords helped me to deliver my perception easier and eliminate confusion with normal user-defined methods.
The syntax can look something like this:
The binding enum should of course has some constraints. For example, the constraint could be implementing a certain class
BindingEnum
which requires overriding a Type getter.Then we can use it as
Usage:
This feature is extremely useful when we know that a certain class will only have a pre-determined set of subclasses, which happens a lot.
Two major benefits:
switch
statement in such scenarios. Which in turn, guarantees covering all possible cases (not possible with a sequence ofif
is
statements).SomeFeatureState
then the subclasses areSomeFeatureInitial
,SomeFeatureLoading
,SomeFeatureIdle
andSomeFeatureError
. By utilizing a feature likeboundTo
, libraries can take advantage of this pattern to reduce boilerplate for the users. ( I don't want to expand much on this particular usage, it's just an example).I'm aware that I'm not suggesting a full-fledged feature; syntax is too verbose, examples are somehow specific, and lack of resources. I'll try to improve my idea but first would like to know wether it's doable.
If this idea doesn't qualify to become a language feature, I'm interested in learning if other languages have such a feature, a workaround in dart, or a term to describe it if it exists (it's different than sealed classes).
The text was updated successfully, but these errors were encountered: