-
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
Allow leading context parameters in extension methods #10940
Conversation
@nicolasstucki If you have more examples where this would be useful, it would be good to try them out, and either use them in the library or add them as test cases. |
It would be good to also add some documentation for this feature. I played a bit with this branch and it looks that when having code like this object Foo
trait Bar
given bar: Bar with {}
extension (using Bar)(foo: Foo.type) def xxxx = 1 you cannot provide the using section explicitly when using extension syntax, e.g. Foo.xxxx(using bar) // won't compile Instead you need to fallback to a dotless method call xxxx(using bar)(Foo) Is this intentional? To me this seems somehow inconsistent as in case of both type parameters and postfix using clauses the parameters of an extension can be passed explicitly in all notations (even though type parameters in the definition of an extension are placed before the extension receiver parameter) extension [A](foo: Foo.type)(using Bar) def yyyy = 1
val a = Foo.yyyy
val b = Foo.yyyy[Int](using bar)
val c = yyyy[Int](Foo)(using bar) Changes introduced in this PR will require some adjustments to code completion as before we could assume that the first parameter of an extension method would always be the receiver, which will not necessarily be true anymore |
Yes, that's intentional. Leading using clauses can be passed only if the method is called directly. The analogy with type parameters is unfortunate, but we plan to change the rules for type parameters so that type parameters can follow both Who will take care of the changes in code completion? |
Ok, that makes sense. I can take care of code completion |
docs/docs/reference/contextual/right-associative-extension-methods.md
Outdated
Show resolved
Hide resolved
docs/docs/reference/contextual/right-associative-extension-methods.md
Outdated
Show resolved
Hide resolved
@odersky I played a bit with this branch and it looks like leading using clauses make the search of extensions in companion objects and givens in scope not work.
but if you look at this example trait Food
case class Banana(color: String) extends Food
trait Diet[A <: Animal]:
type F <: Food
def food: Seq[F]
trait Animal
object Animal:
extension [A <: Animal](using diet: Diet[A])(animal: A) def food1 = diet.food
extension [A <: Animal](animal: A)(using diet: Diet[A]) def food2 = diet.food
extension [A <: Animal](using diet: Diet[A])(animal: A) def food3 = diet.food
extension [A <: Animal](animal: A)(using diet: Diet[A]) def food4 = diet.food
trait Monkey extends Animal
given Diet[Monkey] with
type F = Banana
def food: Seq[Banana] = Seq(new Banana("yellow"), Banana("green"))
trait FoodOps
given FoodOps with
extension [A <: Animal](using diet: Diet[A])(animal: A) def food5 = diet.food
extension [A <: Animal](animal: A)(using diet: Diet[A]) def food6 = diet.food
val monkey = new Monkey {}
val foods = Seq(
monkey.food1, // doesn't compile
monkey.food2, // compiles
monkey.food3, // compiles
monkey.food4, // compiles
monkey.food5, // doesn't compile
monkey.food6, // compiles
) you'll see that for cases 2 and 3 from the spec this doesn't work as expected (as case 4 is kind of a composition of 2 and 3, I guess it doesn't work either). Passing the type parameter to |
Allowing multiple type parameters sections doesn't seem to be going to change anything here. The problem here is that when an extension method is found in an companion object, it is discarded from the search because when the receiver type is compared to the first parameter of the extension, they don't match anymore because the first parameter can now be a using clause. But if the extension is available directly in the current scope a totally different mechanism is used to find it and this works. For extensions located inside implicits the search uses the same mechanism as for implicit conversions. I'm wondering whether there is any particular reason why we don't use the same mechanism to find extensions in all cases |
I added some tests and adapted some code in dokka to use extension methods on reflect symbols directly (this is only a first step). From the reflection use cases perspective it looks good. |
@odersky #10950 will not help with this case as we cannot move the type parameter to the trait Animal
object Animal:
extension [A <: Animal](using diet: Diet[A])(animal: A) def food1 = diet.food
...
extension [A <: Animal](using diet: Diet[A])(animal: A) def food3 = diet.food
...
monkey.food1, // doesn't compile
...
monkey.food3, // compiles
... |
90535f1
to
872082e
Compare
community-build/src/scala/dotty/communitybuild/FieldsImpl.scala
Outdated
Show resolved
Hide resolved
compiler/src-non-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala
Outdated
Show resolved
Hide resolved
2623ed0
to
725822a
Compare
725822a
to
b8e1fb1
Compare
Rebased and resolved confilcts |
b8e1fb1
to
c4b210e
Compare
@odersky can you double-check the commits I added and then merge? |
@nicolasstucki Looks all good now. Thanks for your help! |
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Reflects the changes done in scala#10940
Fixes #9530