-
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
AssertionError: assertion failed with intersections of method type refinements #8736
Comments
Given that it seems we won't be able to support this (#8822), I've attempted to replicate it using unions and match types, it seems to work OK: trait Rec[K <: String] { self =>
protected val map: Map[String, Any]
type ValueOf[A <: K]
def get(k: K): ValueOf[k.type] = map(k).asInstanceOf
def +[K1 <: String](that: Rec[K1]): Rec[K | K1] {
type ValueOf[A <: K | K1] = A match {
case K => self.ValueOf[A]
case K1 => that.ValueOf[A]
}
} = new Rec[K | K1] {
protected val map = self.map ++ that.map
type ValueOf[A <: K | K1] = A match {
case K => self.ValueOf[A]
case K1 => that.ValueOf[A]
}
}
}
object Rec {
def field[S <: String & Singleton, V](s: S)(v: V) : Rec[s.type] {
type ValueOf[K <: s.type] = K match {
case s.type => V
}
} = new Rec[s.type] {
protected val map = Map(s -> v)
type ValueOf[K <: s.type] = K match {
case s.type => V
}
}
}
@main def Test = {
val rec = Rec.field("k")("Str") + Rec.field("v")(0) + Rec.field("z")(true)
def res1: String = rec.get("k")
def res2: Int = rec.get("v")
def res3: Boolean = rec.get("z")
println((res1, res2, res3))
} |
@smarter Unfortunately this type, and probably match types in general, cannot be widened, e.g. this function cannot be applied to def getZ[S >: "z", V](r: Rec[S] { type ValueOf[A <: S] = A match { case "z" => V } }): V =
r.get("z") def res3: Boolean = getZ(rec) Error:
There's another way to formulate this, with extension methods, that one seems to work well. I thought import scala.language.{dynamics, implicitConversions}
class Rec[K <: String, +V](protected val map: Map[String, Any]) extends Dynamic
object Rec {
def field[V](s: String)(v: V): Rec[s.type, V] = new Rec[s.type, V](Map(s -> v))
implicit class RecOps[R <: Rec[_, _]](private val self: R) extends AnyVal {
def get[V](k: String)(implicit ev: R <:< Rec[k.type, V]): V = self.map(k: k.type).asInstanceOf[V]
def selectDynamic[V](k: String)(implicit ev: R <:< Rec[k.type, V]): V = get(k: k.type)
def ++[R1 <: Rec[_, _]](that: R1): R with R1 = new Rec(self.map ++ that.map).asInstanceOf[R with R1]
}
}
def getZ[V](r: Rec["z", V]): V = r.z
@main def Test = {
val rec = Rec.field("k")("Str") ++ Rec.field("v")(0) ++ Rec.field("z")(true)
def res1: String = rec.k
def res2: Int = rec.v
def res3: Boolean = getZ(rec)
println((res1, res2, res3))
} |
This one works :) def getZ[S >: "z" <: String](r: Rec[S]): r.ValueOf["z"] =
r.get("z") |
I tried the original code using function types instead of methods for object App extends App {
trait Rec0[K <: String] {
private[App] val map: Map[String, Any]
def get: (k: K) => Any
}
def Rec0(map0: Map[String, Any]) = new Rec0[String] {
val map = map0
def get: (k: String) => Any = map(_)
}
type Rec[K <: String, V0] = Rec0[K] { def get: (k: K) => V0 }
def field[V](s: String)(v: V): Rec[s.type, V] = Rec0(Map(s -> v)).asInstanceOf[Rec[s.type, V]]
implicit class RecOps[R <: Rec0[_]](has: R) {
def +[K1 <: String, V1](that: Rec[K1, V1]): R with Rec[K1, V1] = Rec0(has.map ++ that.map).asInstanceOf[R with Rec[K1, V1]]
}
def rec:
Rec["k", String]
with Rec["v", Int]
with Rec["z", Boolean]
= {
field("k")("Str") +
field("v")(0) +
field("z")(true)
}
def res1: String = rec.get("k")
def res2: Int = rec.get("v")
def res3: Boolean = rec.get("z")
// error
// def res4: Boolean = rec.get("nofield")
println((res1, res2, res3))
} Dotty complained:
I tried adding the import just to see what happened, but it resulted in a runtime crash:
@smarter I guess something should be done about it, no? The error suggestion was probably misleading, but adding the import should not create unsoundness. |
Please open a separate issue for that, sounds like structural types that return lambdas are just not handled correctly at all |
The following code compiles under Scala 2, all the way back to 2.11.12 (with literal type signatures omitted), but fails to compile in Dotty.
Scala 2 Scastie: https://scastie.scala-lang.org/yq2w0NDqQTm9VboAjm0Dqg
Dotty Scastie: https://scastie.scala-lang.org/JzTLvfBrRbGkKdIa8NrQNA
Minimized code
Output
Expectation
Expected compilation success
The text was updated successfully, but these errors were encountered: