google / reflectable.dart

Reflectable is a Dart library that allows programmers to eliminate certain usages of dynamic reflection by specialization of reflective code to an equivalent implementation using only static techniques. The use of dynamic reflection is constrained in order to ensure that the specialized code can be generated and will have a reasonable size.
https://pub.dev/packages/reflectable
BSD 3-Clause "New" or "Revised" License
374 stars 56 forks source link

Add nullability #280

Closed eernstg closed 2 years ago

eernstg commented 2 years ago

This PR adds methods isNullable, isNonNullable, isPotentiallyNullable, and isPotentiallyNonNullable to TypeMirror, and implements support for it in various ways.

It uses a semantic approach to nullability, such that a mirror of C? where C is a class is considered to be a ClassMirror whose isNullable returns true.

The alternative (syntactic) design would be to have a special NullableTypeMirror which would contain the ClassMirror, and it would then be used to wrap the mirror for C in order to model the type C?.

The semantic model seems more convenient than the syntactic model:

With the syntactic model, we would still need to have the methods isNullable etc. Otherwise, the developer would need to search through three levels of nesting in order to discover that FutureOr<FutureOr<int?>> is nullable and not non-nullable, and it would be necessary to remember which types are nullable on their own (e.g., Null and dynamic) in order to correctly determine that Null is a type which is nullable and not non-nullable. Similarly, if Y is a type variable with bound int? or without a bound then Y is a type which is neither nullable nor non-nullable. So we definitely need to have those methods in any case.

This seems to imply that the syntactic model would just different from the semantic model by having a bunch of objects that developers would need to remember to skip when they are working with types (that is: they would skip all the "?-wrapper-mirrors").

The associated test shows that there are some obvious glaring holes in the support for reflection on types. In particular, we can't reflect on type variables, because there is no way to get the value of a type variable using generated code outside the abstraction that declares the type variable. Similarly, we have no way to decompose a function type (so we can't say that void Function(int) has parameter type int). So type variables and function types are omitted in several cases.

I've chosen to put a comment saying "missing support" in those cases, even though it isn't something that we can hope to fix (any time soon, or perhaps ever). We might want to delete those comments because they are "TODOs out of reach", but it could also be helpful to have them. WDYT?

This PR prepares reflectable for a new release as 3.0.9.

eernstg commented 2 years ago

Thanks!