-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
New syntax for collective extension methods #7917
Conversation
Allow def (c: Circle).circumference: Double alongside def (c: Circle) circumference: Double The syntax with '.' is preferred for normal methods, which have names starting with a letter and which are not declared @infix. Right now, this preference is not enforced.
@bishabosha There's a bunch of semanticdb tests that need to be updated. Can you take care of that? Thanks! |
Like its ancestor #7914, this PR changes docs as well as implementation. To avoid confusion, it should be merged just before the next release. so that we can keep the two in sync. |
Background and MotivationOriginally, Dotty had only regular extension methods (their syntax just got upgraded in #7914). These extension methods are great since they can be defined everywhere and they can be abstract. That aspect enables in particular the nice expression of infix methods in type classes. But there was a usability problem: If several concrete extension methods are defined together their type parameters and leading value parameter have to be repeated for each method. The current solution to extension methods with implicit classes does this much better. Until now we tried several ways to carve out a special case in the syntax for given instances that only define extension methods. But there was always the problem of "false friends": given instances are about types whereas collective extensions describe parameters of extension methods. The two look similar enough to be confusing. That's why I finally bit the bullet in proposing a completely separate syntax for collective extension methods. It makes things a lot clearer. My previous hesitation to claim The extension syntax looks quite similar to what has been added recently to Dart. The main difference is that we still insist on naming the first parameter, whereas Dart repurposes |
Refs dart-lang/language#41 , it's really glad to see Dotty make this move,Dart do have some fancy stuff |
What happens when you refer to
Inside class:
|
@soronpo It's explained by the expansion: |
looks like one of the tests ran by intent failed in the community build factor10/intent#44 |
It's now: ```scala extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } ``` instead of ```scala given listOps: [T](xs: List[T]) extended with { ... }
They now start with `extension_` instead of `given_`.
32ac8be
to
74e56a0
Compare
I like this. I'd prefer if all the main uses of implicits had totally different syntax:
It leads to a different style of learning and teaching. A developer knows when they need an extension method (more or less). They know when they need a low-friction way to convert between types. They know when they need to prove something to the compiler (e.g. something is a subtype of something else). Etc. Even though all these things use the same mechanism, they are all different use-cases and most programmers think and learn in a use-case-oriented way. Indeed, proliferation of use-case specific features is not considered "complexity" by most programmers, even though objectively it inflates the size of the grammar (see C#, Kotlin, etc.). |
Allow def (c: Circle).circumference: Double alongside def (c: Circle) circumference: Double The syntax with '.' is preferred for normal methods, which have names starting with a letter and which are not declared @infix. Right now, this preference is not enforced.
It's now: ```scala extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } ``` instead of ```scala given listOps: [T](xs: List[T]) extended with { ... }
They now start with `extension_` instead of `given_`.
…/dotty into change-extmethods3
c96e7c2
to
14db070
Compare
Rearrange doc pages so that we can merge before the next release.
"extension on" is more precise. The thing after the "of/on" is a parameter. The definition is not an extension "of" this parameter (what does that even mean?), but rather an extension of the parameter's underlying type. Note that Dart also uses "extension on".
Btw, "extension of" and "extension on" work equally well. But seeing as Dart uses "extension on", copying them keeps future, poly Scala/Dart programmers sane. 😄 |
For me, it would be great if the unnnecessary boilerplate is removed because:
The ideal language is easily readable. Now:
My favourite syntax:
Important: this is the extended object, and with this you can access to other extended methods too. My proposal with real code is much less verbose than current implementation. And personally, I prefer extend because you read it "extend Int" instead of "extension for/on Int".
|
I wonder if new syntax will allow for (generalized) type constraints? For example: extension eitherThrowableOps on [A, E <: Throwable](e: Either[E, A]) {
def toTry: Try[A] = e.fold(Try.failure, Try.success)
} or (less preferred) extension eitherThrowableOps on [A, E](e: Either[E, A])(using ev: E <:< Throwable) {
def toTry: Try[A] = this.fold(Try.failure, Try.success)
} Edit: I found in extension methods docs that at least context bounds is allowed for general extension methods, but for collective extensions only example is extension on [T](xs: List[T])(using Ordering[T]) {
def largest(n: Int) = xs.sorted.takeRight(n)
} Is use of context bound is possible there too? |
It's now:
instead of