传递性,或如何在 Scala 中链接泛型隐式
Transitivity, or How To Chain Generic Implicits in Scala
我正在尝试扩展 Miles Sabin 的这篇优秀文章中描述的功能:Unboxed Union Types 以支持 n-ary 类型联合,例如:
def if[T](t: T)(implicit ev: T <<: (Int | String | Symbol | Double)): String = ???
我修改了 Sabin 的代码并编写了我自己的 <:<
运算符版本,如下所示。
object UnboxedTypeUnion extends TypeUnion {
def is[T](t: T)(implicit ev: T <<: (Int | String | Double | Symbol)) =
t match {
case _: Int => "int"
case _: String => "string"
case _: Double => "double"
case _: Symbol => "symbol"
}
// Does not compile
val x = implicitly[Int <<: (Int | String | Double)]
val y = implicitly[Int <<: Not[Not[Not[Not[Int]]]]]
}
trait TypeUnion {
type Not[A] = A => Nothing
type |[A, B] = Not[Not[A] with Not[B]]
sealed abstract class <<:[-A, +B] extends (A => B)
object <<: {
val singleton = new <<:[Any, Any] { override def apply(v1: Any): Any = v1 }
implicit def instance[A]: A <<: A = singleton.asInstanceOf[A <<: A]
implicit def negation[A]: A <<: Not[Not[A]] = singleton.asInstanceOf[A <<: Not[Not[A]]]
implicit def transitivity[A, B, C](implicit ab: A <<: B, bc: B <<: C): A <<: C = singleton.asInstanceOf[A <<: C]
}
}
潜在的问题是每个额外的逻辑析取 (OR
) 将生成的证据子类包装在一个新的双重否定中,即
implicitly[Not[Not[Int]] <<: (Int | String)]
implicitly[Not[Not[Not[Not[Int]]]] <<: (Int | String | Double )]
implicitly[Not[Not[Not[Not[Not[Not[Int]]]]]] <<: (Int | String | Double | Symbol )]
// etc.
理论上,我希望双重否定身份的定义与传递性的定义相结合,以允许它起作用,但是我无法编译它。有谁知道这是否可行,或者递归链接的泛型是否超出了 Scala 编译器的能力?
尝试
type | [A, B]
trait <<: [A, B]
trait LowPriority_<<: {
implicit def monotonicR[A, B, C](implicit ab: A <<: B): A <<: (B | C) = null
}
object <<: extends LowPriority_<<: {
implicit def sym[A]: A <<: A = null
implicit def monotonicL[A, B, C](implicit ab: A <<: B): A <<: (C | B) = null
}
implicitly[Int <<: (Int | String | Double | Long)]
implicitly[String <<: (Int | String | Double | Long)]
implicitly[Double <<: (Int | String | Double | Long)]
implicitly[Long <<: (Int | String | Double | Long)]
// implicitly[Char <<: (Int | String | Double | Long)] // doesn't compile
或
type Not[A] = A => Nothing
trait DisjNot[A] {
type Or[B] = DisjNot[A with Not[B]]
type Build = Not[A]
}
type Disj[A] = DisjNot[Not[A]]
type Disj2[A, B] = Disj[A]#Or[B]#Build
type Disj3[A, B, C] = Disj[A]#Or[B]#Or[C]#Build
type Disj4[A, B, C, D] = Disj[A]#Or[B]#Or[C]#Or[D]#Build
type <<: [A, B] = Not[Not[A]] <:< B
implicitly[Int <<: Disj4[Int, String, Boolean, Double]]
implicitly[String <<: Disj4[Int, String, Boolean, Double]]
implicitly[Boolean <<: Disj4[Int, String, Boolean, Double]]
implicitly[Double <<: Disj4[Int, String, Boolean, Double]]
// implicitly[Char <<: Disj4[Int, String, Boolean, Double]] // doesn't compile
我正在尝试扩展 Miles Sabin 的这篇优秀文章中描述的功能:Unboxed Union Types 以支持 n-ary 类型联合,例如:
def if[T](t: T)(implicit ev: T <<: (Int | String | Symbol | Double)): String = ???
我修改了 Sabin 的代码并编写了我自己的 <:<
运算符版本,如下所示。
object UnboxedTypeUnion extends TypeUnion {
def is[T](t: T)(implicit ev: T <<: (Int | String | Double | Symbol)) =
t match {
case _: Int => "int"
case _: String => "string"
case _: Double => "double"
case _: Symbol => "symbol"
}
// Does not compile
val x = implicitly[Int <<: (Int | String | Double)]
val y = implicitly[Int <<: Not[Not[Not[Not[Int]]]]]
}
trait TypeUnion {
type Not[A] = A => Nothing
type |[A, B] = Not[Not[A] with Not[B]]
sealed abstract class <<:[-A, +B] extends (A => B)
object <<: {
val singleton = new <<:[Any, Any] { override def apply(v1: Any): Any = v1 }
implicit def instance[A]: A <<: A = singleton.asInstanceOf[A <<: A]
implicit def negation[A]: A <<: Not[Not[A]] = singleton.asInstanceOf[A <<: Not[Not[A]]]
implicit def transitivity[A, B, C](implicit ab: A <<: B, bc: B <<: C): A <<: C = singleton.asInstanceOf[A <<: C]
}
}
潜在的问题是每个额外的逻辑析取 (OR
) 将生成的证据子类包装在一个新的双重否定中,即
implicitly[Not[Not[Int]] <<: (Int | String)]
implicitly[Not[Not[Not[Not[Int]]]] <<: (Int | String | Double )]
implicitly[Not[Not[Not[Not[Not[Not[Int]]]]]] <<: (Int | String | Double | Symbol )]
// etc.
理论上,我希望双重否定身份的定义与传递性的定义相结合,以允许它起作用,但是我无法编译它。有谁知道这是否可行,或者递归链接的泛型是否超出了 Scala 编译器的能力?
尝试
type | [A, B]
trait <<: [A, B]
trait LowPriority_<<: {
implicit def monotonicR[A, B, C](implicit ab: A <<: B): A <<: (B | C) = null
}
object <<: extends LowPriority_<<: {
implicit def sym[A]: A <<: A = null
implicit def monotonicL[A, B, C](implicit ab: A <<: B): A <<: (C | B) = null
}
implicitly[Int <<: (Int | String | Double | Long)]
implicitly[String <<: (Int | String | Double | Long)]
implicitly[Double <<: (Int | String | Double | Long)]
implicitly[Long <<: (Int | String | Double | Long)]
// implicitly[Char <<: (Int | String | Double | Long)] // doesn't compile
或
type Not[A] = A => Nothing
trait DisjNot[A] {
type Or[B] = DisjNot[A with Not[B]]
type Build = Not[A]
}
type Disj[A] = DisjNot[Not[A]]
type Disj2[A, B] = Disj[A]#Or[B]#Build
type Disj3[A, B, C] = Disj[A]#Or[B]#Or[C]#Build
type Disj4[A, B, C, D] = Disj[A]#Or[B]#Or[C]#Or[D]#Build
type <<: [A, B] = Not[Not[A]] <:< B
implicitly[Int <<: Disj4[Int, String, Boolean, Double]]
implicitly[String <<: Disj4[Int, String, Boolean, Double]]
implicitly[Boolean <<: Disj4[Int, String, Boolean, Double]]
implicitly[Double <<: Disj4[Int, String, Boolean, Double]]
// implicitly[Char <<: Disj4[Int, String, Boolean, Double]] // doesn't compile