Re: Experimental features
| From: | Alex Wells | Date: | Thu, 06 Oct 2022 16:41:21 +0000 |
| Subject: | Re: Experimental features | ||
| References: | 1 2 3 4 5 6 7 8 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-118769@lists.php.net to get a copy of this message | ||
> On 6 Oct 2022, at 16:06, Rowan Tommins <rowan.collins@gmail.com> wrote:
>
> On 06/10/2022 12:16, Alex Wells wrote:
>> A marker merely just tells the compiler "hey, allow me to use this feature right
>> here", i.e. it denotes a piece of code as allowed to use the feature, not enable it.
>> Effectively, all experimental features are just regular features that are "always on" and
>> hence can be optimized the same way regular features are. The marker serves two purposes: allow
>> simple discovery of usages (by the compiler or other tools) and force the developer to acknowledge
>> they're using an experimental feature with an unstable API/syntax/spec.
>
>
> I don't really understand the distinction between "enabling" and "being
> able to use", nor what it would mean for a feature to "experimental" but also
> integrated fully into the language.
>
> In general, I'm finding it quite hard to follow the idea in the abstract - the definition
> of "feature" seems very fuzzy. Could someone give some examples from another language, or
> concrete examples where it would be used in PHP, and then work through the details of what would be
> expected to happen in what version of PHP?
>
>
>> Regarding the monitoring - that is a problem that needs to be solved and there are multiple
>> solutions: GitHub issues, corporate/public messengers (Slack?) or the internals mailing list.
>
>
> There are certainly ways to approach it; I'm just agreeing with a previous commenter that
> this would need to be an explicit part of any proposal, not just hand-waved away.
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
I'll try my best to elaborate, although I the author of the proposal will likely do a better
job.
A feature is generally considered any addition to the language or it's standard library - that
could be a new set of functions, new syntax etc. Experimental features refer to features whose
stability is "experimental".
Kotlin divides their features into four stability levels: experimental, alpha, beta and stable. Each
of those might have their own preconditions, BC promises and release cycle rules. A feature with
"experimental" stability level requires an opt-in: a piece of code that uses the new
feature must be marked with an annotation. For example, Kotlin has recently introduced a new feature
- unsigned integer types. This inlcudes new types and a new syntax. This is how you'd use them:
```kotlin
@OptIn(ExperimentalUnsignedTypes::class)
fun unsignedInt(): UInt {
return 123u
}
@OptIn(ExperimentalUnsignedTypes::class)
fun main(args: Array<String>) {
println(
unsignedInt()
)
}
```
123u here denotes an integer that is unsigned. The u part is the new
syntax. UInt is the new type. Notice two OptIn annotations - one for the
function which returns the new type, and the other for it's dependent. The second annotation is
needed because of the return type - Kotlin assumes the return type is used, effectively assuming
you're using the new type in your function, which requires opt-in.
This changes if unsignedInt returns Int (a regular type, not part of the
experimental feature) instead:
```kotlin
@OptIn(ExperimentalUnsignedTypes::class)
fun unsignedInt(): Int {
return 123u as Int // cast UInt to Int
}
fun main(args: Array<String>) {
println(
unsignedInt()
)
}
```
Notice how an opt-in is no longer needed for main.
If you omit the opt-in altogether, this is what the compiler (and the IDE) will throw at you:
Main.kt: (7, 20): This declaration is experimental and its usage should be marked with
'@kotlin.ExperimentalUnsignedTypes' or
'@OptIn(kotlin.ExperimentalUnsignedTypes::class)'
Of course, in Kotlin there also mechanism to opt-in more than just a single function. This can be
done on file, module or even project level:
```
@file:OptIn(ExperimentalUnsignedTypes::class)
// or module-wide
sourceSets {
someModule {
languageSettings.optIn("org.mylibrary.OptInAnnotation")
}
}
// or project-wide compiler flag
-opt-in=org.mylibrary.OptInAnnotation
```
////
Onto the PHP application. It's similar, although obviously due to the dynamic nature of the
language the compiler wouldn't be able to force opt-ins nearly on the same level as Kotlin
compiler does. This isn't to say experimental features can't be implemented properly in
PHP.
Let's say PHP wants to experiment with a new function - json_validate(). The idea
is simple: follow the regular RFC process and release the function the same way you would without
experimental functions. The only thing that is needed is to mark the function as one that requires
an opt-in:
```php
#[Experimental(ExperimentalLevel.WARNING)]
class ExperimentalJsonValidate {}
#[RequiresOptIn(ExperimentalJsonValidate::class)]
function json_validate($something): something {}
```
Then, any consumer of that function would be required to opt-in:
```php
#[OptIn(ExperimentalJsonValidate::class)]
function main() {
json_validate(); // all good
}
```
If an opt-in is missing, PHP will trigger an warning (during runtime):
```php
function main() {
json_validate(); // trigger_error('Must be opted-in', E_WARNING)
}
```
Few months after the release (let's say 8.2), PHP collects feedback and analyses codebases
(which is easy thanks to the attribute) and decides it wants to change the name to
json_revalidate. This is of course over exaggerated, but it's to prove the point.
Just as any other change in PHP, this would require an RFC. If it's accepted, the change is
done.
Under regular circumstances (no experimental features), such a change would have to at least have a
deprecation notice for one minor/major release prior to it (so the minimum version this could be
done in is 8.4). Moreover, such changes would usually be avoided at all costs, because this is a big
breaking change.
Experimental features on the other hand are explicit opt-in - users know it can be changed at any
time, without a warning, a deprecation or anything for that matter, in any release. That allows the
change to be done without really worrying of BC and also much earlier (8.3 or even 8.2.1 if you
decide so). Also, the #OptIn attribute allows consumers to easily find all places where
an experimental feature was used in case a breaking change was done.
The same concept can also be applied to new syntax - let's say generics, except with a more
strict error (E_FATAL, for example). This has been briefly discussed here: https://github.com/PHPGenerics/php-generics-rfc/issues/49