Skip to content

Rename scala.implicits.Not to scala.util.Not #9577

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

Merged
merged 1 commit into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ class Definitions {
@tu lazy val TypeBox_CAP: TypeSymbol = TypeBoxClass.requiredType(tpnme.CAP)

@tu lazy val MatchCaseClass: ClassSymbol = requiredClass("scala.internal.MatchCase")
@tu lazy val NotClass: ClassSymbol = requiredClass("scala.implicits.Not")
@tu lazy val NotClass: ClassSymbol = requiredClass("scala.util.Not")
@tu lazy val Not_value: Symbol = NotClass.companionModule.requiredMethod(nme.value)

@tu lazy val ValueOfClass: ClassSymbol = requiredClass("scala.ValueOf")
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@ trait Implicits:
|According to the new implicit resolution rules this is no longer possible;
|the search will fail with a global ambiguity error instead.
|
|Consider using the scala.implicits.Not class to implement similar functionality.""",
|Consider using the scala.util.Not class to implement similar functionality.""",
ctx.source.atSpan(span))

/** A relation that influences the order in which implicits are tried.
Expand Down
16 changes: 8 additions & 8 deletions compiler/test-resources/repl/importFromObj
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ scala> buf += xs
| Required: Int
scala> buf ++= xs
val res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> import util.foo
1 | import util.foo
| ^^^
| value foo is not a member of util
scala> import util.foo.bar
1 | import util.foo.bar
| ^^^^^^^^
| value foo is not a member of util
scala> import util.foobar
1 | import util.foobar
| ^^^^^^
| value foobar is not a member of util
scala> import util.foobar.bar
1 | import util.foobar.bar
| ^^^^^^^^^^^
| value foobar is not a member of util
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ which means that the alternative `c` would be chosen as solution!
Scala 2's somewhat puzzling behavior with respect to ambiguity has been exploited to implement
the analogue of a "negated" search in implicit resolution, where a query `Q1` fails if some
other query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior
these techniques no longer work. But there is now a new special type `scala.implicits.Not`
these techniques no longer work. But there is now a new special type `scala.util.Not`
which implements negation directly. For any query type `Q`: `Not[Q]` succeeds if and only if
the implicit search for `Q` fails.

Expand Down
6 changes: 6 additions & 0 deletions library/src-bootstrapped/scala/implicits/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scala

package object implicits {
@deprecated("scala.implicits.Not has been renamed scala.util.Not", "0.27.0")
type Not[A] = scala.util.Not[A]
}
45 changes: 45 additions & 0 deletions library/src-bootstrapped/scala/util/Not.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package scala.util

/** A special class used to implement negation in implicit search.
*
* Consider the problem of using implicit `i1` for a query type `D` if an implicit
* for some other class `C` is available, and using an implicit `i2` if no implicit
* value of type `C` is available. If we do not want to prioritize `i1` and `i2` by
* putting them in different traits we can instead define the following:
*
* given i1: D(using ev: C) = ...
* given i2: D(using ev: Not[C]) = ...
*
* `Not` is treated specially in implicit search, similar to the way logical negation
* is treated in Prolog: The implicit search for `Not[C]` succeeds if and only if the implicit
* search for `C` fails.
*
* In Scala 2 this form of negation can be simulated by setting up a conditional
* ambiguous implicit and an unconditional fallback, the way it is done with the
* `default`, `amb1` and `amb2` methods below. Due to the way these two methods are
* defined, `Not` is also usable from Scala 2.
*
* In Dotty, ambiguity is a global error, and therefore cannot be used to implement negation.
* Instead, `Not` is treated natively in implicit search.
*/
final class Not[+T] private ()

trait LowPriorityNot {

/** A fallback method used to emulate negation in Scala 2 */
given default[T] as Not[T] = Not.value
}
object Not extends LowPriorityNot {

/** A value of type `Not` to signal a successful search for `Not[C]` (i.e. a failing
* search for `C`). A reference to this value will be explicitly constructed by Dotty's
* implicit search algorithm
*/
def value: Not[Nothing] = new Not[Nothing]()

/** One of two ambiguous methods used to emulate negation in Scala 2 */
given amb1[T](using ev: T) as Not[T] = ???

/** One of two ambiguous methods used to emulate negation in Scala 2 */
given amb2[T](using ev: T) as Not[T] = ???
}