-
Notifications
You must be signed in to change notification settings - Fork 205
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
Views on an object without a wrapper object #1474
Comments
can view extend another view? In TS it would be like |
Yes, a view (as currently proposed) may have an The TS interface mechanism (and TS type aliases) create names for types, and they don't have an associated implementation (they could be implemented in many different ways). That makes them very different from views: Views are inherently tied to a specific implementation, because it's one of the main reasons for having views that they allow for a zero-cost abstraction over an existing underlying "representation" type (the on-type), and this allows view methods to be resolved statically, and, in some cases, inlined. But if we focus on the sets of members only, there are similarities: Multiple |
I have an use case for this in the form of Partial. Typescript has the concept of I hope this could be a thing with views as those have many use cases. Here are a few:
Partial were a game changer for me, I can barely keep breathing now that I don't have those anymore :( |
@cedvdb I guess |
could views be used to somehow make a core type stricter?
|
You'd probably need an |
@eernstg could views statically implement a class and be returned from a method? I've implemented js_bindings that expose the HTML stuff through However, I am not able to correctly expose JS arrays because I can only use I've created a wrapper so you can do I am talking with @sigmundch about it here, maybe this is a case that |
@jodinathan wrote:
Yes, if you box it. Here's a more detailed story, saying the same thing: A view can have an However, view method invocation is resolved statically. This means that with a This again means that even though we have However, we can box the viewed object: So that's probably very nearly the same kind of wrapper as the @sigmundch said the following in the comment you mentioned:
That would indeed be interesting, and perhaps quite useful! The question is how much optimization it would enable. We did actually change for-in statements such that they require an But it might still be worthwhile to consider a relaxation of this rule again, e.g., such that it would be ok to use an |
Thanks @eernstg for taking a look. Precisely, your thoughts here are very aligned with I'm suggesting. In this comment I raise a similar but more general question: are there other places where a language feature asks for Other than If we go down this route, would it make sense to have additional declarations to express conformance to an API? for example: abstract view ForInArgument {
Iterator iterator;
}
class Iterable conforms ForInArgument { ... } // classes can conform a static API
view JSArray conforms ForInArgument { ... } // static-views can also conform a static API Unlike implementing an interface, this conformance cannot doesn't create an abstraction at the use site. The expansion of the use site is more like a macro, where the lookup of members is resolved entirely based on the static type of the udnerlying expression. That is, in |
maybe change the |
I believe that explicit inheritance or implementation was preferable to an implicit structure. Compare the That covers cases where you are writing the class. So long as there is a way to extend existing classes to implement an interface (like extensions or views), being explicit is almost always better. |
@sigmundch wrote:
I agree with @Levi-Lesches that the connection to an explicitly declared type is good for correctness and for readability, and I think we might not need to rely very much on structural typing. In particular, a for-in statement could require that the iteratee has
It is known statically which one of those is applicable, so we can generate different code for the three cases. With this approach, it is indicated explicitly (in the class type or in the view type) that it is intended to support usage as an "iterable" object by means of an "iterator" object, and it allows (1) the traditional semantics, purely OO, (2) an approach where we run a view method to obtain an The following is probably buggy, but it illustrates the idea: view JSArrayIterable<X> on JSArray<X> {
JSArrayIterator<X> get iterator => _JSArrayIterator(this);
}
view JSArrayIterator<X> on _JSArrayIterator<X> implements Iterator<X> {
X get current => ...; // JS magic that looks up jSArray[index].
bool moveNext() => ++index < length;
}
class _JSArrayIterator<X> {
final JSArray<X> jSArray;
int index = 0;
int length; // It might be useful to cache the length?
_JSArrayIterator(this.jSArray): length = jSArray.length;
} |
I think we are all in agreement :) - that's why I was asking for the notion of "conformance", so we can statically express that the @eernstg - my suggestion for If we decide to overload the |
The current views proposal uses One reason why |
@sigmundch, I made an attempt to develop the ideas about allowing for-in statements to work on an object which has an
To explain the first constraint a bit more, we do not want to box the object. That would yield a regular object which is typable as In contrast, the purpose of this discussion is to improve the performance by "lowering" the for-in statement to work on the on-type instance, based on the view methods (which may well be inlined). We could simply require that a collection which is iterated over in a for-in statement must be assignable to class C<X> ... {...}
view CAsIteratable<X> on C<X> {
Iterator<X> get iterator => ...;
} This means that we relax the static check on for-in statements, but we preserve the static type constraint that we must be able to obtain a proper However, we could also allow the class C<X> ... {...}
view CAsIteratable<X> on C<X> implements Iterable<X> {
CAsIterator<X> get iterator => this;
.. // Other `Iterable` methods.
}
view CAsIterator<X> on C<X> implements Iterator<X> {
bool moveNext() => ...;
X current => ...;
} This would allow us to lower the for-in statement even more, because we could both obtain and use the iterator based on view methods (and we'd never have to allocate an actual object of type However, this gets awfully close to the old ("untyped") approach where we did not require anything from a collection in order to be able to iterate over it in a for-in statement (other than it should have an So I proposed some new rules for the The core of this relationship is the following kind of "lifting" relation: abstract class A {
A get next;
}
view V on ... implements A {
V get next => ...; // `V get next` can implement `A get next` because `V implements A`.
} We can do this (e.g., as proposed in #2150), but the recursion is highly incomplete in the following sense: We can maintain the relationship for return types, but it is hardly useful for formal parameter types, and it immediately gets out of hand for any higher-order cases. For example, we can allow The underlying issue is that the ability to "lift" a signature in this sense amounts to a limited amount of auto-boxing (so the boxed object would implement In other words, the investigation of the situation that gave rise to #2150 has shown that it is at least difficult to generalize the approach that we've discussed here (which could actually be used with for-in statements) to a more wholesome language mechanism. |
Add a proposal exploring the possibility of addressing #1474 while hewing closely to existing Dart concepts. Essentially, we add a restricted form of classes ("structs") which are essentially data classes with primary constructors; and then a further restricted form of structs ("extension structs") which provide wrapper-less views on an object.
Filed feature issue #2727 for this. Please continue the discussion there. |
Extension methods (#41) can extend the set of instance members of an interface type T with a set of members declared outside of T. We may describe this as a zero-cost abstraction, because the denoted instance of type T is used directly (without an additional wrapper object, or any other kind of run-time support), and because the extension members are resolved statically (such that extension member invocation can be even faster than instance method invocation). In short, extension methods allow us to add convenience functions operating on instances of T, and invoking them using the syntax of instance member invocations.
We may also wish to replace the set M of instance members of an interface type T by a set Malternate of members declared outside of T. The two sets may overlap, but the point is that some of the instance members of T should not be used, or they should only be used in a specific way. A zero-cost abstraction mechanism that allows us to perform this replacement could help enforcing this particular kind of discipline without lowering the performance: If an instance member shouldn't be invoked at all then it would be omitted from Malternate, and if certain instance members can be called, but only in specific ways, then there would be some members in Malternate whose bodies will call those instance members in the appropriate manner. In short, this kind of abstraction would allow us to impose an added discipline on the use of an instance of type T and invoke them using the syntax of instance members.
For instance, if a given JSON value is modeled using instances of
List<dynamic>
,Map<String, dynamic>
,bool
,String
, or numbers, and the JSON value is intended to conform to a given schema, then we could use this kind of abstraction to ensure that the object representing the JSON value is not modified (by not including any mutating members), and that it is only accessed in a way which is consistent with the schema (by providing member declarations that will access the parts of the value according to their schemas).The text was updated successfully, but these errors were encountered: