-
Notifications
You must be signed in to change notification settings - Fork 362
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
Opt-in requirements (aka Experimental API support) #95
Comments
Looks good! I'm myself developing a few experimental libraries, including one already released for BluetoothGatt on Android relying on kotlinx.coroutines, and this would be really useful. |
There is "Same module exemption" but what if my library consist of multiple modules? Is there way to avoid propagation requirement except suppressing an error? |
It's a bit late, so dare with me if I overlooked a phrase. I couldn't find how signature usage and body usage differ in propagation. Or lies the only difference in same module exemption? |
Another question to clear up propagation. The proposal does not mention which access levels need propagation. I assume it is public and protected (where protected makes only sense for an open type), right? |
Generally, if you're using an experimental API, you must propagate it. Otherwise, your clients will not be aware of the fact that something may break, and may break unexpectedly. From this perspective, it's irrelevant whether two modules (where a module is a single self-contained compilation unit) comprise a library in any sense, or not. |
There are two key differences between body and signature usages, highlighted in the proposal:
Visibility modifiers have no effect on the propagation requirements. The reason is that even a private declaration that uses something experimental might be called indirectly from client code, via some public declaration:
|
But there is a difference because all the usages are inside of a single project (but different modules) so it a user depends on the same version of my modules then there is no risk so I'd like to force kotlinx-corotunes:
So I'd like to use experimental core's functions in io's implementation but avoid poisoning all my io's stable API functions |
@cy6erGn0m We cannot guarantee that the user depends on the same version of these two modules though. If the user depends on kotlinx-coroutines-io version 4.2, and gets kotlinx-coroutines-core version 4.3 via a transitive dependency, any usage of a function from kotlinx-coroutines-io may break because the underlying experimental implementation was changed binary incompatibly. Whereas the user doesn't expect any breakage because
|
I've updated the proposal after an internal discussion:
|
To avoid conflict with the Annotation.annotationClass extension from kotlin-stdlib
A few minor updates:
|
FYI the prototype has landed into Kotlin master and 1.2.30. Although it's a 1.3-only API, it's possible to use it with Note that because Kotlin 1.3 is not yet released, using |
Looks like there is a way to make these declarations experimental themselves after all
We've had one more look at this proposal internally and decided to greatly simplify it, or otherwise it was getting out of hand pretty fast. The major change is that we now do not intend to make experimental declarations "poisonous" and verify it in the compiler as much as possible. Because of this simplification, the concept of We've also discussed a bit how we are going to use The prototype of the new simplified approach is currently being worked on. |
See Kotlin/KEEP#95 (comment) Drop Experimental.changesMayBreak, Experimental.Impact, the concept of signature/body usage, same module exemption. Make the majority of tests single-module because there is now no difference in the checker between usages from the same module or from another module
See Kotlin/KEEP#95 (comment) Drop Experimental.changesMayBreak, Experimental.Impact, the concept of signature/body usage, same module exemption. Make the majority of tests single-module because there is now no difference in the checker between usages from the same module or from another module
See Kotlin/KEEP#95 (comment) Drop Experimental.changesMayBreak, Experimental.Impact, the concept of signature/body usage, same module exemption. Make the majority of tests single-module because there is now no difference in the checker between usages from the same module or from another module
It made little sense in the first version of the proposal, but now there's no reason to prevent using experimental API in type alias declaration
Looks like there is a way to make these declarations experimental themselves after all
It made little sense in the first version of the proposal, but now there's no reason to prevent using experimental API in type alias declaration
Usually declaring an experimental annotation requires to spell a long list of targets where it makes sense:
It's easy to forget this incantation and get an experimental marker annotation applicable where it should not have been. What if we imply these targets by default for the annotations marked with |
@ilya-g Sounds like a good idea to me, although a bit too implicit for Kotlin. Please report an issue. |
The feature as currently available in 1.3 is quite interesting and clearly solves a problem. However, I feel it is still quite limited in scope. In particular feature effectively introduces custom visibility scopes - see for example how kotlinx.serialization uses it to limit the reflection API - it is not actually experimental, it doesn't work properly on non-JVM targets). Thinking about this I had an idea how this can be extended. Why not allow for proper user-defined scopes. You would have the ability to define a custom scope as annotation, specify visibility (and possibly warning/error level). Then the annotation can be used on various identifiers to determine a rich scope. The effective visibility would be the intersection between the declared regular visibility modifier and the annotation - if the code is protected the annotation does not widen it, but an inheriting class cannot access it either without the annotation either - if the annotation is internal this makes a symbol annotated as
The semantics of the annotation and the
Limitations:
What do you think about this idea? |
Hi, is there a way to use multiple experimental APIs on the same class/method? @UseExperimental(ExperimentalCoroutinesApi::class)
@UseExperimental(KtorExperimentalAPI::class) // <- error here: not repeatable annotation
private suspend fun connectToSocket() { ... Using Kotlin 1.3.10 |
Can't you pass multiple classes in UseExeprimental annotation constructor?
…On Tue, Nov 20, 2018, 1:36 PM Antanas A. ***@***.***> wrote:
Hi, is there a way to use multiple experimental API's on the same
class/method?
Currently I'm trying to set couple @UseExperimental annotations but IDE
says:
"This annotation is not repeatable"
e.g.:
@UseExperimental(ExperimentalCoroutinesApi::class)
@UseExperimental(KtorExperimentalAPI::class) // <- error here: not repeatable annotation
private suspend fun connectToSocket() { ...
Using Kotlin 1.3.10
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#95 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AGpvBQzDXQXY3pdF5pnlb3Ocs-lovxeUks5uw_djgaJpZM4RNwF7>
.
|
Lol, sorry. I've missed that its argument is "vararg". IntelliJ not suggesting this. |
Hi, I was thinking about that UseExperimental annotation and I think the better approach would be omit "vararg" and make it repeatable, because: b) Usage of experimental APIs is not predictable, at first I can use ObsoleteCoroutinesAPI and few days later use some KtorInternalAPI. So to add new UseExperimental is more convenient instead of adding more arguments in existing UseExperimental. Also git merge requests will look better. c) Now I have an option to add some experimental APIs usage on a whole class and on methods so I'm using multiple experimental APIs and not using "varargs". So if I want to use one more experimental API in a method I'll need pass multiple arguments for UseExperimental. Some UseExperimental now have one argument (for class) and some mulitple arguments (for method). Wouldn't be better to remove "vararg" support from UseExperimental and only make it repeatable? |
@antanas-arvasevicius One problem with your suggestion is that the JVM 1.6 target does not support repeated annotations. The retention of the annotation should however be at least class (so that the compiler can verify API levels on usage). The current API seems to be the most elegant solution. |
@pdvrieze Android Studio 3+ should support repeatable annotations. See: https://developer.android.com/studio/write/java8-support |
@pdvrieze Sorry for the late answer. I'm afraid I didn't completely understand how |
@udalov I'll try to provide some examples. Instead of:
we would generalize this to
However the idea is more extensive. One can define any kind of scope:
What are the benefits:
Considerations:
|
@pdvrieze Thank you for the elaborate example and the explanation! Do I understand it correctly that the compiler uses It seems to me that if the user manually adds Regarding this point:
I agree that the annotated API is not experimental and thus marking it as |
@udalov Indeed brittleVar would be actually private in bytecode. In the case of private it may not be that worthwhile, but for visibilities such as internal it forces all users to be at most internal (but they can still be private) in bytecode. Private is there for completeness. Most of the difference indeed is in the name. I can see the point about not mixing it, but in some cases you may want to limit a scope to a certain application. Using the annotation scope does not work as it relates to the declaration of the scope, not the use of it. My point is mainly to rename, but also to broaden it up a bit. Although that technically breaks the annotations for semantics aspect. Btw. protected internal would be mangled as internal, but protected in the bytecode. The issue is quite close to my idea indeed. I'm not sure about misuse as wording is important, but it doesn't misuse semantics. |
@pdvrieze Please share your naming suggestions in the comments to KT-26216, we'll come back to them when discussing whether and how to rename Regarding the visibility-altering behavior of an annotation -- I'm pretty sure this would lead to lots of issues in the compiler and tooling, the main reason of which is that you'd need to resolve and type-check all annotations before you can deduce the actual visibility of a declaration. In fact, the compiler even might need visibility of a declaration before resolving annotations right now in some cases, I can't be sure because such basic information was always available lexically, before any resolution happens. I think it would also complicate the ability of people reading the source code: in case the visibility is not evident from the scope annotation's name, it's pretty hard to understand whether the declaration you're looking at is public or private. To be sure, you'll need to check the source of all annotations on that declaration in the worst case. As such, I don't think this would be a good addition to this feature. However, if you have any other suggestions on how to broaden the feature without introducing this sort of problems, we'll be glad to discuss them -- please share in KT-26216 as well. Thanks! |
@udalov I've added a short comment tot he bug. On the visibility altering behaviour, perhaps I've not been sufficiently clear on how it works. Effectively it works as an upper bound on the visibility. It should work as follows: if a visibility modifier has been specified at the use site (not the custom scope) it will either be used or fail to compile (violates the bound). If no visibility modifier is specified (default public) then there are two options: either derive the visibility from the annotation, or check the visibility of the annotation and throw an error in case the visibility of the scope is not public. In all cases the actual visibility will be recorded in the visibility attributes of the method exactly the same way it is done already. The more complex case is that of protected internal. This does not exist on Java bytecode level, but might be able to benefit from the same mechanism applied to regular internal. This could/should still be recorded in the extended Kotlin signature. Key is that all visibility is resolved/determined at compilation of the scope use site and recorded in the usual way - so everything is visible/final in the method/class/.. declaration and does not depend on scope resolution. It might be that explicit visibility is better than default (except for public as is now) from a readability perspective and I don't mind dropping the default system - I still think it can helpful to be able to limit a scope to a visibility on its application (of course the annotation itself can have limited visibility too). Scope usage thus at first does not need to resolve the scope annotation at all, it merely needs to compare the locally available scope/api with the one(s) declared on the symbol (potentially) being used. Only the currently already available error/warning option needs resolving/loading the annotation. |
We've discussed this proposal internally once again and decided to change it in the following way:
|
The rename seems like a good idea, it also matches other uses of the capability. |
The annotations are renamed too: `Experimental` -> `RequiresOptIn`, `UseExperimental` -> `OptIn`. This is needed to facilitate usage of this mechanism in other related areas, such as restricted, internal, obsolete APIs etc. See KT-26216. Also, add message to `RequiresOptIn`, see KT-28872.
Is require mean that compiler should report error instead? Issue about this behavior KT-37507 |
Discussion of the proposal:
https://github.com/Kotlin/KEEP/blob/master/proposals/experimental.md
The text was updated successfully, but these errors were encountered: