From 1db6732579c24873bd767062010803f68a1730d9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 14 Mar 2018 19:03:36 +0100 Subject: [PATCH 1/8] Relax restriction about type overrides Allow several type aliases if they are the same. --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- .../src/dotty/tools/dotc/typer/RefChecks.scala | 5 ++++- tests/pos/this-types.scala | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/pos/this-types.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 30a1c4b1aa3b..562bcc80c6cd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -989,7 +989,7 @@ object Types { case tp: TypeRef => if (tp.symbol.isClass) tp else tp.info match { - case TypeAlias(tp) => tp.dealias1(keepAnnots): @tailrec + case TypeAlias(alias) => alias.dealias1(keepAnnots): @tailrec case _ => tp } case app @ AppliedType(tycon, args) => diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index a0ad815fa6e1..2d0ae6dd29f8 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -343,7 +343,10 @@ object RefChecks { if (autoOverride(member) || other.owner.is(JavaTrait) && ctx.testScala2Mode("`override' modifier required when a Java 8 default method is re-implemented", member.pos)) member.setFlag(Override) - else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) + else if (member.isType && self.memberInfo(member) =:= self.memberInfo(other)) + () // OK, don't complain about type aliases which are equal + else if (member.owner != clazz && other.owner != clazz && + !(other.owner derivesFrom member.owner)) emitOverrideError( clazz + " inherits conflicting members:\n " + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) diff --git a/tests/pos/this-types.scala b/tests/pos/this-types.scala new file mode 100644 index 000000000000..d75de54b3ffa --- /dev/null +++ b/tests/pos/this-types.scala @@ -0,0 +1,16 @@ +trait A { + type A_This <: A +} +trait B extends A { + type A_This = B_This + type B_This <: B +} +trait C extends A { + type A_This = C_This + type C_This <: C +} +trait D extends B with C { + type B_This = D_This + type C_This = D_This + type D_This <: D +} \ No newline at end of file From c13f6f43ed1101ca42a7e8584bd1e6c8a582033c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 18 Mar 2018 11:43:23 +0100 Subject: [PATCH 2/8] New typeclass encoding --- tests/pos/typeclass-encoding2.scala | 187 ++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 tests/pos/typeclass-encoding2.scala diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala new file mode 100644 index 000000000000..2fcb774a5c10 --- /dev/null +++ b/tests/pos/typeclass-encoding2.scala @@ -0,0 +1,187 @@ +/** A possible type class encoding for + + trait SemiGroup { + def add(that: This): This + } + + trait Monoid extends SemiGroup + common { + def unit: This + } + + extension IntOps for Int : Monoid { + def add(that: Int) = this + that + } + common { + def unit = 0 + } + + extension StringOps for String : Monoid { + def add(that: Int) = this ++ that + } + common { + def unit = "" + } + + def sum[T: Monoid](xs: List[T]): T = + (instance[T, Monoid].unit /: xs)(_ `add` _) + +*/ +object runtime { + + trait TypeClass { + val common: TypeClassCommon + type This = common.This + } + + trait TypeClassCommon { self => + type This + type Instance <: TypeClass + def inject(x: This): Instance { val common: self.type } + } + + trait Extension[From, To <: TypeClass] extends TypeClassCommon { + type This = From + type Instance = To + } + + implicit def inject[From](x: From) + (implicit ev: Extension[From, _]): ev.Instance { type This = From } = + ev.inject(x) +} +import runtime._ + +object semiGroups { + + trait SemiGroup extends TypeClass { + import common._ + def add(that: This): This + } + trait SemiGroupCommon extends TypeClassCommon { + type Instance <: SemiGroup + } + + trait Monoid extends SemiGroup { + val common: MonoidCommon + import common._ + } + trait MonoidCommon extends SemiGroupCommon { + type Instance <: Monoid + def unit: This + } + + implicit object IntOps extends Extension[Int, Monoid] with MonoidCommon { self => + type This = Int + type Instance = Monoid + def unit: Int = 0 + def inject($this: Int) = new Monoid { + val common: self.type = self + def add(that: This): This = $this + that + } + } + + implicit object StringOps extends Extension[String, Monoid] with MonoidCommon { self => + type This = String + type Instance = Monoid + def unit = "" + def inject($this: String) = new Monoid { + val common: self.type = self + def add(that: This): This = $this.concat(that) + } + } + + def sum[T](xs: List[T])(implicit $ev: Extension[T, Monoid] with MonoidCommon) = + (implicitly[Extension[T, Monoid] with MonoidCommon].unit /: xs)((x, y) => x `add` y) +} + +/** Encoding for + + trait Ord { + def compareTo(that: This): Int + def < (that: This) = compareTo(that) < 0 + def > (that: This) = compareTo(that) > 0 + } + common { + val minimum: This + } + + extension IntOrd for Int : Ord { + def compareTo(that: Int) = + if (this < that) -1 else if (this > that) +1 else 0 + } + common { + val minimum = Int.MinValue + } + + extension ListOrd[T : Ord] for List[T] : Ord { + def compareTo(that: List[T]): Int = (this, that) match { + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs, y :: ys) => + val fst = x.compareTo(y) + if (fst != 0) fst else xs.compareTo(ys) + } + } + common { + val minimum = Nil + } + + def min[T: Ord](x: T, y: T) = if (x < y) x else y + + def inf[T: Ord](xs: List[T]): T = (instance[T, Ord].minimum /: xs)(_ `min` _) +*/ + +object ord { + + trait Ord extends TypeClass { + import common._ + def compareTo(that: This): Int + def < (that: This) = compareTo(that) < 0 + def > (that: This) = compareTo(that) > 0 + } + trait OrdCommon extends TypeClassCommon { + type Instance <: Ord + def minimum: This + } + + implicit object IntOrd extends Extension[Int, Ord] with OrdCommon { self => + type This = Int + type Instance = Ord + val minimum: Int = Int.MinValue + def inject($this: Int) = new Ord { + val common: self.type = self + def compareTo(that: This): Int = + if (this < that) -1 else if (this > that) +1 else 0 + } + } + + class ListOrd[T](implicit ev: Extension[T, Ord] with OrdCommon) + extends Extension[List[T], Ord] with OrdCommon { self => + type This = List[T] + type Instance = Ord + def minimum: List[T] = Nil + def inject($this: List[T]) = new Ord { + val common: self.type = self + def compareTo(that: List[T]): Int = ($this, that) match { + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs, y :: ys) => + val fst = x.compareTo(y) + if (fst != 0) fst else xs.compareTo(ys) + } + } + } + + implicit def listOrd[T](implicit ev: Extension[T, Ord] with OrdCommon): ListOrd[T] = + new ListOrd[T] + + def min[T](x: T, y: T)(implicit ev: Extension[T, Ord] with OrdCommon): T = + if (x < y) x else y + + def inf[T](xs: List[T])(implicit ev: Extension[T, Ord] with OrdCommon): T = { + val smallest = implicitly[Extension[T, Ord] with OrdCommon].minimum + (smallest /: xs)(min(_, _)) + } +} \ No newline at end of file From 7680fc7aec906a927fba77d0e95c934a5c64f81f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 18 Mar 2018 15:39:35 +0100 Subject: [PATCH 3/8] Fix two crashers One involved selections on WildcardTypes, the other taking the base type of refinement classes. Both manifested themselves in neg/typeclass-encoding2.scala --- .../tools/dotc/core/SymDenotations.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 1 + tests/neg/typeclass-encoding2.scala | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/neg/typeclass-encoding2.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 955f1a34342c..d3db4845e8b3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1466,7 +1466,7 @@ object SymDenotations { val builder = new BaseDataBuilder for (p <- classParents) { if (p.typeSymbol.isClass) builder.addAll(p.typeSymbol.asClass.baseClasses) - else assert(ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") + else assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") } (classSymbol :: builder.baseClasses, builder.baseClassSet) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 562bcc80c6cd..313228557776 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1967,6 +1967,7 @@ object Types { def derivedSelect(prefix: Type)(implicit ctx: Context): Type = if (prefix eq this.prefix) this else if (prefix.isBottomType) prefix + else if (prefix.isInstanceOf[WildcardType]) WildcardType else if (isType) { val res = if (currentSymbol.is(ClassTypeParam)) argForParam(prefix) diff --git a/tests/neg/typeclass-encoding2.scala b/tests/neg/typeclass-encoding2.scala new file mode 100644 index 000000000000..d0fd4534e586 --- /dev/null +++ b/tests/neg/typeclass-encoding2.scala @@ -0,0 +1,26 @@ +// Used to crash with: +// assertion failed: class has non-class parent +// Once that was fixed, another crash with: +// assertion failed: invalid prefix WildcardType(NoType) +object runtime1 { + + trait TypeClass1[A] { + val common: TypeClassCommon1 + type This[X] = common.This[X] + } + + trait TypeClassCommon1 { self => + type This[X] + type Instance[X] <: TypeClass1[X] + def inject[A](x: This[A]): Instance[A]// { val common: self.type } + } + + trait Extension1[From[_], To[X] <: TypeClass1[X]] extends TypeClassCommon1 { + type This[X] = From[X] + type Instance[X] = To[X] + } + + implicit def inject[A, From[_]](x: From[A]) + (implicit ev: Extension1[From, _]): ev.Instance[A] { type This[X] = From[X] } = + ev.inject(x) // error: found: ev.To[A], required: ev.To[A]{This = From} +} \ No newline at end of file From 2664cc10be3b1180daee544b691c756cb6a09f38 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 18 Mar 2018 19:32:19 +0100 Subject: [PATCH 4/8] Fix hasUnknownMembers Sometimes a computation of a superType will give NoType. Keeping with the spirit of unKnownMembers to say yes when in doubt, we should return true in this case. The change is necessary to make the recursive call of `develop` and the call to `flatMap` in `flatten` in typeclass-encoding2.scala typecheck. --- .../dotty/tools/dotc/typer/ProtoTypes.scala | 1 + tests/pos/typeclass-encoding2.scala | 243 +++++++++++++----- 2 files changed, 184 insertions(+), 60 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 3f47d650a77f..f3bc499e8555 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -105,6 +105,7 @@ object ProtoTypes { private def hasUnknownMembers(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeVar => !tp.isInstantiated case tp: WildcardType => true + case NoType => true case tp: TypeRef => val sym = tp.symbol sym == defn.NothingClass || diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala index 2fcb774a5c10..21ff97024cd9 100644 --- a/tests/pos/typeclass-encoding2.scala +++ b/tests/pos/typeclass-encoding2.scala @@ -1,31 +1,30 @@ /** A possible type class encoding for - trait SemiGroup { - def add(that: This): This - } - - trait Monoid extends SemiGroup - common { - def unit: This - } + trait SemiGroup extends TypeClass { + def add(that: This): This + } - extension IntOps for Int : Monoid { - def add(that: Int) = this + that - } - common { - def unit = 0 - } + trait Monoid extends SemiGroup + common { + def unit: This + } - extension StringOps for String : Monoid { - def add(that: Int) = this ++ that - } - common { - def unit = "" - } + extension IntOps for Int : Monoid { + def add(that: Int) = this + that + } + common { + def unit = 0 + } - def sum[T: Monoid](xs: List[T]): T = - (instance[T, Monoid].unit /: xs)(_ `add` _) + extension StringOps for String : Monoid { + def add(that: Int) = this ++ that + } + common { + def unit = "" + } + def sum[T: Monoid](xs: List[T]): T = + (Monod.impl[T].unit /: xs)(_ `add` _) */ object runtime { @@ -40,6 +39,11 @@ object runtime { def inject(x: This): Instance { val common: self.type } } + trait TypeClassCompanion { + type Impl[T] <: Extension[T, _] + def impl[T](implicit ev: Impl[T]): Impl[T] = ev + } + trait Extension[From, To <: TypeClass] extends TypeClassCommon { type This = From type Instance = To @@ -54,12 +58,16 @@ import runtime._ object semiGroups { trait SemiGroup extends TypeClass { + val common: SemiGroupCommon import common._ def add(that: This): This } trait SemiGroupCommon extends TypeClassCommon { type Instance <: SemiGroup } + object SemiGroup extends TypeClassCompanion { + type Impl[T] = Extension[T, SemiGroup] with SemiGroupCommon + } trait Monoid extends SemiGroup { val common: MonoidCommon @@ -69,6 +77,9 @@ object semiGroups { type Instance <: Monoid def unit: This } + object Monoid extends TypeClassCompanion { + type Impl[T] = Extension[T, Monoid] with MonoidCommon + } implicit object IntOps extends Extension[Int, Monoid] with MonoidCommon { self => type This = Int @@ -90,51 +101,51 @@ object semiGroups { } } - def sum[T](xs: List[T])(implicit $ev: Extension[T, Monoid] with MonoidCommon) = - (implicitly[Extension[T, Monoid] with MonoidCommon].unit /: xs)((x, y) => x `add` y) + def sum[T](xs: List[T])(implicit $ev: Monoid.Impl[T]) = + (Monoid.impl[T].unit /: xs)((x, y) => x `add` y) } /** Encoding for - trait Ord { - def compareTo(that: This): Int - def < (that: This) = compareTo(that) < 0 - def > (that: This) = compareTo(that) > 0 - } - common { - val minimum: This - } + trait Ord extends TypeClass { + def compareTo(that: This): Int + def < (that: This) = compareTo(that) < 0 + def > (that: This) = compareTo(that) > 0 + } + common { + val minimum: This + } - extension IntOrd for Int : Ord { - def compareTo(that: Int) = - if (this < that) -1 else if (this > that) +1 else 0 - } - common { - val minimum = Int.MinValue - } + extension IntOrd for Int : Ord { + def compareTo(that: Int) = + if (this < that) -1 else if (this > that) +1 else 0 + } + common { + val minimum = Int.MinValue + } - extension ListOrd[T : Ord] for List[T] : Ord { - def compareTo(that: List[T]): Int = (this, that) match { - case (Nil, Nil) => 0 - case (Nil, _) => -1 - case (_, Nil) => +1 - case (x :: xs, y :: ys) => - val fst = x.compareTo(y) - if (fst != 0) fst else xs.compareTo(ys) + extension ListOrd[T : Ord] for List[T] : Ord { + def compareTo(that: List[T]): Int = (this, that) match { + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs, y :: ys) => + val fst = x.compareTo(y) + if (fst != 0) fst else xs.compareTo(ys) + } + } + common { + val minimum = Nil } - } - common { - val minimum = Nil - } - def min[T: Ord](x: T, y: T) = if (x < y) x else y + def min[T: Ord](x: T, y: T) = if (x < y) x else y - def inf[T: Ord](xs: List[T]): T = (instance[T, Ord].minimum /: xs)(_ `min` _) + def inf[T: Ord](xs: List[T]): T = (Ord.impl[T].minimum /: xs)(min) */ - object ord { trait Ord extends TypeClass { + val common: OrdCommon import common._ def compareTo(that: This): Int def < (that: This) = compareTo(that) < 0 @@ -144,6 +155,9 @@ object ord { type Instance <: Ord def minimum: This } + object Ord extends TypeClassCompanion { + type Impl[T] = Extension[T, Ord] with OrdCommon + } implicit object IntOrd extends Extension[Int, Ord] with OrdCommon { self => type This = Int @@ -156,7 +170,7 @@ object ord { } } - class ListOrd[T](implicit ev: Extension[T, Ord] with OrdCommon) + class ListOrd[T](implicit ev: Ord.Impl[T]) extends Extension[List[T], Ord] with OrdCommon { self => type This = List[T] type Instance = Ord @@ -174,14 +188,123 @@ object ord { } } - implicit def listOrd[T](implicit ev: Extension[T, Ord] with OrdCommon): ListOrd[T] = + implicit def listOrd[T](implicit ev: Ord.Impl[T]): ListOrd[T] = new ListOrd[T] - def min[T](x: T, y: T)(implicit ev: Extension[T, Ord] with OrdCommon): T = + def min[T](x: T, y: T)(implicit ev: Ord.Impl[T]): T = if (x < y) x else y - def inf[T](xs: List[T])(implicit ev: Extension[T, Ord] with OrdCommon): T = { - val smallest = implicitly[Extension[T, Ord] with OrdCommon].minimum - (smallest /: xs)(min(_, _)) + def inf[T](xs: List[T])(implicit ev: Ord.Impl[T]): T = { + val smallest = Ord.impl[T].minimum + (smallest /: xs)(min) + } +} + +/** Encoding for + + trait Functor[A] extends TypeClass1 { + def map[B](f: A => B): This[B] + } + common { + def pure[A](x: A): This[A] + } + + // Generically, `pure[A]{.map(f)}^n` + def develop[A, F[X] : Functor[X]](n: Int, f: A => A): F[A] = + if (n == 0) Functor.impl[F].pure[A] + else develop[A, F](n - 1, f).map(f) + + trait Monad[A] extends Functor[A] { + def flatMap[B](f: A => This[B]): This[B] + def map[B](f: A => B) = this.flatMap(f.andThen(pure)) + } + + extension ListMonad[T] for List[T] : Monad[T] { + static def pure[A] = Nil + + def flatMap[B](f: A => List[B]): List[B] = this match { + case x :: xs => f(x) ++ xs.flatMap(f) + case Nil => Nil + } + } + + extension MonadFlatten[T[X]: Monad[X]] for T[T[A]] { + def flatten: T[A] = this.flatMap(identity) + } +*/ +object runtime1 { + + trait TypeClass1 { + val common: TypeClassCommon1 + type This = common.This + } + + trait TypeClassCommon1 { self => + type This[X] + type Instance[X] <: TypeClass1 + def inject[A](x: This[A]): Instance[A] { val common: self.type } + } + + trait TypeClassCompanion1 { + type Impl[T[_]] <: Extension1[T, _] + def impl[T[_]](implicit ev: Impl[T]): Impl[T] = ev + } + + trait Extension1[From[_], To[X] <: TypeClass1] extends TypeClassCommon1 { + type This[X] = From[X] + type Instance[X] = To[X] + } + + implicit def inject1[A, From[_]](x: From[A]) + (implicit ev: Extension1[From, _]): ev.Instance[A] { type This = From } = + ev.inject(x) +} +import runtime1._ + +object functors { + + trait Functor[A] extends TypeClass1 { + val common: FunctorCommon + import common._ + def map[B](f: A => B): This[B] + } + trait FunctorCommon extends TypeClassCommon1 { + type Instance[X] <: Functor[X] + def pure[A](x: A): This[A] + } + object Functor extends TypeClassCompanion1 { + type Impl[T[_]] = Extension1[T, Functor] with FunctorCommon + } + + trait Monad[A] extends Functor[A] { + val common: MonadCommon + import common._ + def flatMap[B](f: A => This[B]): This[B] + def map[B](f: A => B) = this.flatMap(f.andThen(common.pure)) + } + trait MonadCommon extends FunctorCommon { + type Instance[X] <: Monad[X] + } + object Monad extends TypeClassCompanion1 { + type Impl[T[_]] = Extension1[T, Monad] with MonadCommon + } + + def develop[A, F[X]](n: Int, x: A, f: A => A)(implicit ev: Functor.Impl[F]): F[A] = + if (n == 0) Functor.impl[F].pure(x) + else develop(n - 1, x, f).map(f) + + implicit object ListMonad extends Extension1[List, Monad] with MonadCommon { self => + type This[A] = List[A] + type Instance = Monad + def pure[A](x: A) = x :: Nil + def inject[A]($this: List[A]) = new Monad[A] { + val common: self.type = self + def flatMap[B](f: A => List[B]): List[B] = $this.flatMap(f) + } + } + + object MonadFlatten { + def flatten[T[_], A]($this: T[T[A]])(implicit ev: Monad.Impl[T]): T[A] = + $this.flatMap(identity ) } } \ No newline at end of file From 0462b45e36b39c3ce4ad5d982d690b2f3ced12ae Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 18 Mar 2018 22:16:47 +0100 Subject: [PATCH 5/8] Avoid names object self types --- tests/pos/typeclass-encoding2.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala index 21ff97024cd9..fd3807ef658a 100644 --- a/tests/pos/typeclass-encoding2.scala +++ b/tests/pos/typeclass-encoding2.scala @@ -81,22 +81,22 @@ object semiGroups { type Impl[T] = Extension[T, Monoid] with MonoidCommon } - implicit object IntOps extends Extension[Int, Monoid] with MonoidCommon { self => + implicit object IntOps extends Extension[Int, Monoid] with MonoidCommon { type This = Int type Instance = Monoid def unit: Int = 0 def inject($this: Int) = new Monoid { - val common: self.type = self + val common: IntOps.this.type = IntOps.this def add(that: This): This = $this + that } } - implicit object StringOps extends Extension[String, Monoid] with MonoidCommon { self => + implicit object StringOps extends Extension[String, Monoid] with MonoidCommon { type This = String type Instance = Monoid def unit = "" def inject($this: String) = new Monoid { - val common: self.type = self + val common: StringOps.this.type = StringOps.this def add(that: This): This = $this.concat(that) } } @@ -159,12 +159,12 @@ object ord { type Impl[T] = Extension[T, Ord] with OrdCommon } - implicit object IntOrd extends Extension[Int, Ord] with OrdCommon { self => + implicit object IntOrd extends Extension[Int, Ord] with OrdCommon { type This = Int type Instance = Ord val minimum: Int = Int.MinValue def inject($this: Int) = new Ord { - val common: self.type = self + val common: IntOrd.this.type = IntOrd.this def compareTo(that: This): Int = if (this < that) -1 else if (this > that) +1 else 0 } @@ -293,12 +293,12 @@ object functors { if (n == 0) Functor.impl[F].pure(x) else develop(n - 1, x, f).map(f) - implicit object ListMonad extends Extension1[List, Monad] with MonadCommon { self => + implicit object ListMonad extends Extension1[List, Monad] with MonadCommon { type This[A] = List[A] type Instance = Monad def pure[A](x: A) = x :: Nil def inject[A]($this: List[A]) = new Monad[A] { - val common: self.type = self + val common: ListMonad.this.type = ListMonad def flatMap[B](f: A => List[B]): List[B] = $this.flatMap(f) } } From 32665933e9a832ad1262f1445dccaa90a8b6a6e3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 18 Mar 2018 22:27:56 +0100 Subject: [PATCH 6/8] Remove duplication in derivedSelect --- .../src/dotty/tools/dotc/core/Types.scala | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 313228557776..6b68ea100c12 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1967,38 +1967,35 @@ object Types { def derivedSelect(prefix: Type)(implicit ctx: Context): Type = if (prefix eq this.prefix) this else if (prefix.isBottomType) prefix - else if (prefix.isInstanceOf[WildcardType]) WildcardType - else if (isType) { - val res = - if (currentSymbol.is(ClassTypeParam)) argForParam(prefix) - else prefix.lookupRefined(name) - if (res.exists) res - else if (Config.splitProjections) - prefix match { - case prefix: AndType => - def isMissing(tp: Type) = tp match { - case tp: TypeRef => !tp.info.exists - case _ => false - } - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return ( - if (isMissing(derived1)) derived2 - else if (isMissing(derived2)) derived1 - else prefix.derivedAndType(derived1, derived2)) - case prefix: OrType => - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return prefix.derivedOrType(derived1, derived2) - case _ => - withPrefix(prefix) - } + else { + if (isType) { + val res = + if (currentSymbol.is(ClassTypeParam)) argForParam(prefix) + else prefix.lookupRefined(name) + if (res.exists) return res + if (Config.splitProjections) + prefix match { + case prefix: AndType => + def isMissing(tp: Type) = tp match { + case tp: TypeRef => !tp.info.exists + case _ => false + } + val derived1 = derivedSelect(prefix.tp1) + val derived2 = derivedSelect(prefix.tp2) + return ( + if (isMissing(derived1)) derived2 + else if (isMissing(derived2)) derived1 + else prefix.derivedAndType(derived1, derived2)) + case prefix: OrType => + val derived1 = derivedSelect(prefix.tp1) + val derived2 = derivedSelect(prefix.tp2) + return prefix.derivedOrType(derived1, derived2) + case _ => + } + } + if (prefix.isInstanceOf[WildcardType]) WildcardType else withPrefix(prefix) } - else prefix match { - case _: WildcardType => WildcardType - case _ => withPrefix(prefix) - } /** A reference like this one, but with the given symbol, if it exists */ final def withSym(sym: Symbol)(implicit ctx: Context): ThisType = From cb093054e39ac0e86195279d14ddca126c89b654 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 19 Mar 2018 09:22:18 +0100 Subject: [PATCH 7/8] Another tweak to hasUnknownMembers Needed to make latest instance of typeclass-encoding2 pass. Compared to previous this one has a single type parameter for `Extension`. --- .../dotty/tools/dotc/typer/ProtoTypes.scala | 1 + tests/pos/typeclass-encoding2.scala | 36 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index f3bc499e8555..85a828b9a401 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -115,6 +115,7 @@ object ProtoTypes { bound.isProvisional && hasUnknownMembers(bound) } } + case tp: AppliedType => hasUnknownMembers(tp.tycon) || hasUnknownMembers(tp.superType) case tp: TypeProxy => hasUnknownMembers(tp.superType) case _ => false } diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala index fd3807ef658a..4022758536f0 100644 --- a/tests/pos/typeclass-encoding2.scala +++ b/tests/pos/typeclass-encoding2.scala @@ -40,17 +40,16 @@ object runtime { } trait TypeClassCompanion { - type Impl[T] <: Extension[T, _] + type Impl[T] <: Extension[T] def impl[T](implicit ev: Impl[T]): Impl[T] = ev } - trait Extension[From, To <: TypeClass] extends TypeClassCommon { + trait Extension[From] extends TypeClassCommon { type This = From - type Instance = To } implicit def inject[From](x: From) - (implicit ev: Extension[From, _]): ev.Instance { type This = From } = + (implicit ev: Extension[From]): ev.Instance { type This = From } = ev.inject(x) } import runtime._ @@ -66,7 +65,7 @@ object semiGroups { type Instance <: SemiGroup } object SemiGroup extends TypeClassCompanion { - type Impl[T] = Extension[T, SemiGroup] with SemiGroupCommon + type Impl[T] = Extension[T] with SemiGroupCommon } trait Monoid extends SemiGroup { @@ -78,10 +77,10 @@ object semiGroups { def unit: This } object Monoid extends TypeClassCompanion { - type Impl[T] = Extension[T, Monoid] with MonoidCommon + type Impl[T] = Extension[T] with MonoidCommon } - implicit object IntOps extends Extension[Int, Monoid] with MonoidCommon { + implicit object IntOps extends Extension[Int] with MonoidCommon { type This = Int type Instance = Monoid def unit: Int = 0 @@ -91,7 +90,7 @@ object semiGroups { } } - implicit object StringOps extends Extension[String, Monoid] with MonoidCommon { + implicit object StringOps extends Extension[String] with MonoidCommon { type This = String type Instance = Monoid def unit = "" @@ -101,7 +100,7 @@ object semiGroups { } } - def sum[T](xs: List[T])(implicit $ev: Monoid.Impl[T]) = + def sum[T](xs: List[T])(implicit ev: Monoid.Impl[T]) = (Monoid.impl[T].unit /: xs)((x, y) => x `add` y) } @@ -156,10 +155,10 @@ object ord { def minimum: This } object Ord extends TypeClassCompanion { - type Impl[T] = Extension[T, Ord] with OrdCommon + type Impl[T] = Extension[T] with OrdCommon } - implicit object IntOrd extends Extension[Int, Ord] with OrdCommon { + implicit object IntOrd extends Extension[Int] with OrdCommon { type This = Int type Instance = Ord val minimum: Int = Int.MinValue @@ -171,7 +170,7 @@ object ord { } class ListOrd[T](implicit ev: Ord.Impl[T]) - extends Extension[List[T], Ord] with OrdCommon { self => + extends Extension[List[T]] with OrdCommon { self => type This = List[T] type Instance = Ord def minimum: List[T] = Nil @@ -246,17 +245,16 @@ object runtime1 { } trait TypeClassCompanion1 { - type Impl[T[_]] <: Extension1[T, _] + type Impl[T[_]] <: Extension1[T] def impl[T[_]](implicit ev: Impl[T]): Impl[T] = ev } - trait Extension1[From[_], To[X] <: TypeClass1] extends TypeClassCommon1 { + trait Extension1[From[_]] extends TypeClassCommon1 { type This[X] = From[X] - type Instance[X] = To[X] } implicit def inject1[A, From[_]](x: From[A]) - (implicit ev: Extension1[From, _]): ev.Instance[A] { type This = From } = + (implicit ev: Extension1[From]): ev.Instance[A] { type This = From } = ev.inject(x) } import runtime1._ @@ -273,7 +271,7 @@ object functors { def pure[A](x: A): This[A] } object Functor extends TypeClassCompanion1 { - type Impl[T[_]] = Extension1[T, Functor] with FunctorCommon + type Impl[T[_]] = Extension1[T] with FunctorCommon } trait Monad[A] extends Functor[A] { @@ -286,14 +284,14 @@ object functors { type Instance[X] <: Monad[X] } object Monad extends TypeClassCompanion1 { - type Impl[T[_]] = Extension1[T, Monad] with MonadCommon + type Impl[T[_]] = Extension1[T] with MonadCommon } def develop[A, F[X]](n: Int, x: A, f: A => A)(implicit ev: Functor.Impl[F]): F[A] = if (n == 0) Functor.impl[F].pure(x) else develop(n - 1, x, f).map(f) - implicit object ListMonad extends Extension1[List, Monad] with MonadCommon { + implicit object ListMonad extends Extension1[List] with MonadCommon { type This[A] = List[A] type Instance = Monad def pure[A](x: A) = x :: Nil From e64c62cf6065536cb79638004e0fdc64bf3e8d98 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 19 Mar 2018 11:24:45 +0100 Subject: [PATCH 8/8] Extending and polishing the example - Add straight extensions of type classes, i.e. implementation inherits type class directly. - Rename common as an identifier to commons - Drop `Extension` type. --- tests/pos/typeclass-encoding2.scala | 138 ++++++++++++++++++---------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala index 4022758536f0..3269430435de 100644 --- a/tests/pos/typeclass-encoding2.scala +++ b/tests/pos/typeclass-encoding2.scala @@ -1,4 +1,4 @@ -/** A possible type class encoding for +/** 1. Simple type classes with monomorphic implementations and direct extensions. trait SemiGroup extends TypeClass { def add(that: This): This @@ -23,33 +23,42 @@ def unit = "" } + enum Nat extends Monoid { + case Z + case S(n: Nat) + + def add(that: Nat): Nat = this match { + case S => that + case S(n) => S(n.add(that)) + } + } + common { + def unit = Z + } + def sum[T: Monoid](xs: List[T]): T = (Monod.impl[T].unit /: xs)(_ `add` _) */ object runtime { trait TypeClass { - val common: TypeClassCommon - type This = common.This + val commons: TypeClassCommon + type This = commons.This } trait TypeClassCommon { self => type This type Instance <: TypeClass - def inject(x: This): Instance { val common: self.type } + def inject(x: This): Instance { val commons: self.type } } trait TypeClassCompanion { - type Impl[T] <: Extension[T] + type Impl[T] <: TypeClassCommon { type This = T } def impl[T](implicit ev: Impl[T]): Impl[T] = ev } - trait Extension[From] extends TypeClassCommon { - type This = From - } - implicit def inject[From](x: From) - (implicit ev: Extension[From]): ev.Instance { type This = From } = + (implicit ev: TypeClassCommon { type This = From }): ev.Instance { type This = From } = ev.inject(x) } import runtime._ @@ -57,54 +66,79 @@ import runtime._ object semiGroups { trait SemiGroup extends TypeClass { - val common: SemiGroupCommon - import common._ + val commons: SemiGroupCommon + import commons._ def add(that: This): This } trait SemiGroupCommon extends TypeClassCommon { type Instance <: SemiGroup } object SemiGroup extends TypeClassCompanion { - type Impl[T] = Extension[T] with SemiGroupCommon + type Impl[T] = SemiGroupCommon { type This = T } } trait Monoid extends SemiGroup { - val common: MonoidCommon - import common._ + val commons: MonoidCommon + import commons._ } trait MonoidCommon extends SemiGroupCommon { type Instance <: Monoid def unit: This } object Monoid extends TypeClassCompanion { - type Impl[T] = Extension[T] with MonoidCommon + type Impl[T] = MonoidCommon { type This = T } } - implicit object IntOps extends Extension[Int] with MonoidCommon { + implicit object IntOps extends MonoidCommon { type This = Int type Instance = Monoid def unit: Int = 0 def inject($this: Int) = new Monoid { - val common: IntOps.this.type = IntOps.this + val commons: IntOps.this.type = IntOps.this def add(that: This): This = $this + that } } - implicit object StringOps extends Extension[String] with MonoidCommon { + implicit object StringOps extends MonoidCommon { type This = String type Instance = Monoid def unit = "" def inject($this: String) = new Monoid { - val common: StringOps.this.type = StringOps.this + val commons: StringOps.this.type = StringOps.this def add(that: This): This = $this.concat(that) } } + enum Nat extends Monoid { + case Z + case S(n: Nat) + + def add(that: Nat): Nat = this match { + case Z => that + case S(n) => S(n.add(that)) + } + + val commons: Nat.type = Nat + } + object Nat extends MonoidCommon { + type This = Nat + type Instance = Nat + def unit = Nat.Z + def inject($this: Nat) = $this + } + import Nat.{Z, S} + + implicit def NatOps: Nat.type = Nat + def sum[T](xs: List[T])(implicit ev: Monoid.Impl[T]) = (Monoid.impl[T].unit /: xs)((x, y) => x `add` y) + + sum(List(1, 2, 3)) + sum(List("hello ", "world!")) + sum(List(Z, S(Z), S(S(Z)))) } -/** Encoding for +/** 2. Generic implementations of simple type classes. trait Ord extends TypeClass { def compareTo(that: This): Int @@ -144,8 +178,8 @@ object semiGroups { object ord { trait Ord extends TypeClass { - val common: OrdCommon - import common._ + val commons: OrdCommon + import commons._ def compareTo(that: This): Int def < (that: This) = compareTo(that) < 0 def > (that: This) = compareTo(that) > 0 @@ -155,27 +189,28 @@ object ord { def minimum: This } object Ord extends TypeClassCompanion { - type Impl[T] = Extension[T] with OrdCommon + type Impl[T] = OrdCommon { type This = T } } - implicit object IntOrd extends Extension[Int] with OrdCommon { + implicit object IntOrd extends OrdCommon { type This = Int type Instance = Ord val minimum: Int = Int.MinValue def inject($this: Int) = new Ord { - val common: IntOrd.this.type = IntOrd.this + val commons: IntOrd.this.type = IntOrd.this + import commons._ def compareTo(that: This): Int = if (this < that) -1 else if (this > that) +1 else 0 } } - class ListOrd[T](implicit ev: Ord.Impl[T]) - extends Extension[List[T]] with OrdCommon { self => + class ListOrd[T](implicit ev: Ord.Impl[T]) extends OrdCommon { self => type This = List[T] type Instance = Ord def minimum: List[T] = Nil def inject($this: List[T]) = new Ord { - val common: self.type = self + val commons: self.type = self + import commons._ def compareTo(that: List[T]): Int = ($this, that) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -197,9 +232,13 @@ object ord { val smallest = Ord.impl[T].minimum (smallest /: xs)(min) } + + inf(List[Int]()) + inf(List(List(1, 2), List(1, 2, 3))) + inf(List(List(List(1), List(2)), List(List(1), List(2), List(3)))) } -/** Encoding for +/** 3. Higher-kinded type classes trait Functor[A] extends TypeClass1 { def map[B](f: A => B): This[B] @@ -234,27 +273,23 @@ object ord { object runtime1 { trait TypeClass1 { - val common: TypeClassCommon1 - type This = common.This + val commons: TypeClassCommon1 + type This = commons.This } trait TypeClassCommon1 { self => type This[X] type Instance[X] <: TypeClass1 - def inject[A](x: This[A]): Instance[A] { val common: self.type } + def inject[A](x: This[A]): Instance[A] { val commons: self.type } } trait TypeClassCompanion1 { - type Impl[T[_]] <: Extension1[T] + type Impl[T[_]] <: TypeClassCommon1 { type This = T } def impl[T[_]](implicit ev: Impl[T]): Impl[T] = ev } - trait Extension1[From[_]] extends TypeClassCommon1 { - type This[X] = From[X] - } - implicit def inject1[A, From[_]](x: From[A]) - (implicit ev: Extension1[From]): ev.Instance[A] { type This = From } = + (implicit ev: TypeClassCommon1 { type This = From }): ev.Instance[A] { type This = From } = ev.inject(x) } import runtime1._ @@ -262,8 +297,8 @@ import runtime1._ object functors { trait Functor[A] extends TypeClass1 { - val common: FunctorCommon - import common._ + val commons: FunctorCommon + import commons._ def map[B](f: A => B): This[B] } trait FunctorCommon extends TypeClassCommon1 { @@ -271,38 +306,41 @@ object functors { def pure[A](x: A): This[A] } object Functor extends TypeClassCompanion1 { - type Impl[T[_]] = Extension1[T] with FunctorCommon + type Impl[T[_]] = FunctorCommon { type This = T } } trait Monad[A] extends Functor[A] { - val common: MonadCommon - import common._ + val commons: MonadCommon + import commons._ def flatMap[B](f: A => This[B]): This[B] - def map[B](f: A => B) = this.flatMap(f.andThen(common.pure)) + def map[B](f: A => B) = this.flatMap(f.andThen(commons.pure)) } trait MonadCommon extends FunctorCommon { type Instance[X] <: Monad[X] } object Monad extends TypeClassCompanion1 { - type Impl[T[_]] = Extension1[T] with MonadCommon + type Impl[T[_]] = MonadCommon { type This = T } } def develop[A, F[X]](n: Int, x: A, f: A => A)(implicit ev: Functor.Impl[F]): F[A] = if (n == 0) Functor.impl[F].pure(x) else develop(n - 1, x, f).map(f) - implicit object ListMonad extends Extension1[List] with MonadCommon { - type This[A] = List[A] + implicit object ListMonad extends MonadCommon { + type This = List type Instance = Monad def pure[A](x: A) = x :: Nil def inject[A]($this: List[A]) = new Monad[A] { - val common: ListMonad.this.type = ListMonad + val commons: ListMonad.this.type = ListMonad + import commons._ def flatMap[B](f: A => List[B]): List[B] = $this.flatMap(f) } } object MonadFlatten { - def flatten[T[_], A]($this: T[T[A]])(implicit ev: Monad.Impl[T]): T[A] = + def flattened[T[_], A]($this: T[T[A]])(implicit ev: Monad.Impl[T]): T[A] = $this.flatMap(identity ) } + + MonadFlatten.flattened(List(List(1, 2, 3), List(4, 5))) } \ No newline at end of file