Skip to content
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

It's inconvenient to have to define typedefs at top-level if you only need them locally. #67

Open
lrhn opened this issue Oct 31, 2018 · 6 comments
Labels
request Requests to resolve a particular developer problem

Comments

@lrhn
Copy link
Member

lrhn commented Oct 31, 2018

Currently typedef declaration can only occur as top-level declarations in a library.

In some cases, you would want to just create a local shorthand for some code. To do that, you now have to put the declaration at the top level, probably with a private name to avoid it leaking, and then use it far from the declaration. That gives bad code locality and convenience of writing.

I propose that typedefs should be allowed "anywhere", or At least at top level, as a static class declaration and as a local declaration in a method body. Effectively anywhere a constant can be declared.
The scoping should be the same as for other declarations. In particular, a local declaration has access to class and method type variables. Since typedefs are structural aliases, that should not be a problem, the declaration can be expanded at the point of use without issue.

With dart-lang/sdk#66, this becomes even more useful.

Example:

class MyFuture<T> implements Future<T> {
  Future<T> catchError(Function handler,  [bool test(Object error)]) {
    typedef Unary = Function(Null);
    if (handler is Unary) handler = (o, StackTrace stack) => handler(i);
    ... handler ..
  }
}

I'm sure there are lots of details to get right, like whether static typedefs need the static in front, but something like this would be nice.

@lrhn lrhn added the request Requests to resolve a particular developer problem label Oct 31, 2018
@lrhn lrhn changed the title It's inconveient to have to define typedefs at top-level if you only need them locally. It's inconvenient to have to define typedefs at top-level if you only need them locally. Oct 31, 2018
@leafpetersen
Copy link
Member

The key question is whether they inhabit the class namespace or not. That is, can you do:

class A {
  typedef Foo = int
}
A.Foo x;

If not, this should be very straightforward. If not, things probably get interesting.

@lrhn
Copy link
Member Author

lrhn commented Nov 9, 2018

If you can write A.Foo, then the typedef is static. We can definitely do that.
The question is whether we allow instance-typedefs and local typedefs.

I do want local typedefs, which means that the typedef itself has access to type variables.

Instance typedefs make less sense. You would not be able to access them through an instance (you can't have a type expression written that way), but they could be accessible inside the class and have access to class type parameters.
It's probably safer to not go there, and only allow static class-scoped typedefs.
(Then we have to decide whether to make class-scoped typedefs automatically static, or to require a static prefix like we do for constants).

@eernstg
Copy link
Member

eernstg commented Nov 9, 2018

I think the interesting part is whether you can use the formal type parameters from the enclosing class:

class List<E> {
  typedef This = List<E>;
  typedef Test = bool Function(E);
  ...
  This sublist(int start, [int end]);
  This where(Test test);
  bool every(Test test); 
}

main() {
  List<int>.Test myTest = $ > 0; // OK, concise lambdas is a different topic ;-)
  ...
}

and subclasses:

class A<X> {
  typedef F = List<X> Function(Map<int, X>);
}

class B implements A<String> {
  F f; // Has type `List<String> Function(Map<int, String>)`.
}

@eernstg
Copy link
Member

eernstg commented Nov 9, 2018

Also note the existing SDK issue on this topic: #2952.

@lrhn
Copy link
Member Author

lrhn commented Mar 28, 2023

Reading this today, the arguments for why allowing static type aliases accessible from the outside as ClassName.TypeDefName doesn't hold any more.

Since we've added general type aliases, you can access statics through those, so accessing nested type aliases from the outside would produce (effectively) nested scopes:

class _NestedScope {
  static int value = 42;
}
class Outer {
  static typedef Inner = _NestedScope;
}
void main() {
  print(Outer.Inner.value);
}

So, either start allowing nested scopes in general, so people won't emulate it with type aliases, or disallow accessing type nested aliases from outside its scope.

@Hixie
Copy link

Hixie commented Jul 11, 2023

As per my example in #3200, I think records make this even more important because they encourage the use of complicated local types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

4 participants