dart-lang / language

Design of the Dart language
Other
2.65k stars 202 forks source link

Allow imports in part files #519

Open juliocbcotta opened 5 years ago

juliocbcotta commented 5 years ago

Currently, when generating code, the part file can't have specific imports and they need to be added in the hand written file.

This behavior restricts the ergonomics of code generation in: 1) Clients of code generating libraries need to update their hand written files with imports. 2) The hand written files have references to imports that are not used in that file.

Use case: A library split in two artifacts.

The code generating library that need to check the Platform, something like Platform.isAndroid in the generated code.

This means : The library client developer will need to add import 'dart:io'; to the hand written code to make the project compile as this is the import that contains the Platform class.

As I understand this need to declare by the imports "by hand" is meant to avoid a missing dependency in the project, but this could be avoid if the annotation library declares if it declares the package dependency.

The concrete example of the problem can be found here.

lrhn commented 5 years ago

The solution usually suggested is to not use parts. Instead use a separate library with its own imports.

The only reason to use a part file, instead of just a library, is to allow access to private members of other parts of the same library. If you need that, and need extra imports, then there is currently no solution. This can happen for generated code (a serializer might need access to private fields, and might need access to dart:convert for JSON encoding).

Adding imports to parts would definitely make the imports only be visible inside that part. This is a new kind of scope (the declaration scope of the part can see the import scope of itself, and of its parent library, and the declaration scope of the parent library, and it adds its declarations to the parent library's declaration scope, but not its imports. Not even prefixed imports. Will imports shadow the main library declarations? Probably not. So, we'll have a lexical scope structure that is (main-imports, part-imports, declarations) in the part and (main-imports, declarations) in the main library, which means that the declarations do not have a single fixed parent scope any more).

The alternative approach to solving this problem would be to allow some libraries to access private members of other libraries, sharing private namespace between two or more "friend" libraries. The difference would be that you could import the other libraries directly, not just as parts of the "main" library, otherwise it would be equivalent to part files with imports.

Sharing private namespace is not a feature we'd allow one library to opt-in to without the consent of the other libraries. We have privacy for a reason, and allowing anyone to override it just because it might be convenient, is a very big foot-gun. So, we'll want every library to mention the other libraries as friends. Then we are pretty much back to the part/part of relations, only without a single most-important library. The "friend" relations declarations would have to be a complete bi-directional graph for the involved libraries (all the libraries in a group must friend all the other libraries in the group, otherwise it's a compile-time error).

So, I think imports in part files is probably a better solution than friend libraries.

MatrixDev commented 3 years ago

are there any plans on implementing this? issue is in open state for quite a bit of time now :(

cedvdb commented 1 year ago

Is there a proposal for the "friend" libraries ?

I sometimes encounter the following (but it might very well be a design mistake):

class A {
   int _a;
}

class B {
   int _b;
}

class C {
   // need to access B._b, and A._a; C being in the same package, but I only want C to be able to access those properties.
}

I could make C the upper part file but that wouldn't fit how I do things (again could be an misdesign on my part). So I wouldn't want this feature if that would encourage misdesign.

A more practical example would be

class TimeRange {
   Time _start;
   Time _end;
}

class DateRange {
   Date _start;
   Date _end;
}

class Schedule {
   DateTime get checkIn => // can't access needed properties :(
   DateTime get checkOut => // can't do.
   DateRange _selectedDateRange;
   TimeRange _selectedTimeRange;
}

I usually use some mapper methods for those cases but it's ugly and only intended for the "friend".

Levi-Lesches commented 1 month ago

There's a proposal for this at https://github.com/dart-lang/language/blob/main/working/augmentation-libraries/parts_with_imports.md