Skip to content

Coherent type classes #2046

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

Closed
wants to merge 2 commits into from
Closed
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: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,8 @@ class Definitions {
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
lazy val CoherentType: TypeRef = ctx.requiredClassRef("scala.typeclass.Coherent")
def CoherentClass(implicit ctx: Context) = CoherentType.symbol.asClass
lazy val LanguageModuleRef = ctx.requiredModule("scala.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ object Denotations {

/** The unique alternative of this denotation that satisfies the predicate `p`,
* or NoDenotation if no satisfying alternative exists.
* @throws TypeError if there is at more than one alternative that satisfies `p`.
* @throws TypeError if there is more than one alternative that satisfies `p`.
*/
def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
* class A extends C[A] with D
* class B extends C[B] with D with E
*
* we approximate `A | B` by `C[A | B] with D`
* we approximate `A | B` by `C[A | B] & D`
*/
def orDominator(tp: Type): Type = {

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,8 @@ trait Implicits { self: Typer =>
/** Convert a (possibly empty) list of search successes into a single search result */
def condense(hits: List[SearchSuccess]): SearchResult = hits match {
case best :: alts =>
alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match {
if (pt.derivesFrom(defn.CoherentClass)) best
else alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match {
case Some(alt) =>
typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}")
/* !!! DEBUG
Expand Down
12 changes: 12 additions & 0 deletions library/src/scala/typeclass/Coherent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package scala.typeclass

/** If a type class C extends this trait, the compiler is allowed
* to assume that for every type instance of `C`, all implicit
* instances of this type instance behave the same way.
*
* Conversely, an implicit resolution of a coherent type class will
* never give an ambiguity error. If there are several candidates
* and none is more specific than the others, an arbitrary candidate
* is chosen.
*/
trait Coherent
34 changes: 34 additions & 0 deletions tests/run/coherent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
trait Base[T] extends scala.typeclass.Coherent {
def value: T
}

class Sub1[T](val value: T) extends Base[T]
class Sub2[T](val value: T) extends Base[T]

object Test {
implicit class ValueDeco[T: Base](x: T) {
def value = implicitly[Base[T]].value
}
def f[T](t: T)(implicit ev1: Sub1[T], ev2: Sub2[T]) = {
// the next four lines give errors if `Base` is not declared coherent
assert(t.value == 2)
assert(implicitly[Base[T]].value == 2)
assert(implicitly[Base[T]].value == 2)
assert(t.value == 2)
}
implicit val s1: Sub1[Int] = new Sub1(2)
implicit val s2: Sub2[Int] = new Sub2(3) // This is not coherent, just used to show that first alternative will be chosen.

def main(args: Array[String]) = {
f(1)
}
}

trait DL extends scala.typeclass.Coherent
trait TL extends DL
trait CL extends DL

object Driving {
def drive(implicit dl: DL) = ()
def f(implicit tl: TL, cl: CL) = drive
}