在 scala shapeless 中,是否可以使用文字类型作为泛型类型参数?
In scala shapeless, is it possible to use literal type as a generic type parameter?
假设我正在编写一个向量乘法程序。按照本文要求:
https://etrain.github.io/2015/05/28/type-safe-linear-algebra-in-scala
只有当两个向量的维数相等时,乘法才能编译成功。为此,我定义了一个通用类型 Axis
,它使用无形文字类型(维数)作为类型参数:
import shapeless.Witness
trait Axis extends Serializable
case object UnknownAxis extends Axis
trait KnownAxis[W <: Witness.Lt[Int]] extends Axis {
def n: Int
def ++(that: KnownAxis[W]): Unit = {}
}
object KnownAxis {
val w1 = Witness(1)
val w2 = Witness(2)
case class K1(n: Witness.`1`.T) extends KnownAxis[w1.type]
case class K2(n: Witness.`2`.T) extends KnownAxis[w2.type]
// K2(2) ++ K1(1) // doesn't compile
K2(2) ++ K2(2)
}
到目前为止一切顺利,但是当我尝试将其概括为所有 n:
时,问题出现了
case class KN[W <: Witness.Lt[Int]](n: W#T) extends KnownAxis[W]
KN(1)
以上代码触发编译错误:
Axis.scala:36: type mismatch;
found : Int(1)
required: this.T
[ERROR] KN(1)
[ERROR] ^
[ERROR] one error found
我的问题是:为什么 Spark 不能专注于更精细的类型 Witness.`1`.T
,而是使用类型 Int
?如何覆盖此行为才能成功定义 case class KN
?
更新 1: 跟进已移至新问题:
When using the singleton type feature of Scala shapeless, how to force the compiler to use narrow/singleton type as an implicit parameter?
Scala 不能在给定 W#T
的情况下推断出 W
也就不足为奇了,因为一般来说两个不同的 W
有可能具有相同的 W#T
.不适用于见证类型,但编译器不会对它们进行特殊处理。
我期望的工作(但不确定为什么不工作)是指定类型参数:
KN[Witness.`1`](1)
// error: type arguments [scala.this.Any] do not conform to method apply's type parameter bounds [W <: shapeless.this.Witness.Lt[scala.this.Int]]
或更有可能
KN[w1.type](1)
// error: type mismatch;
// found : scala.this.Int(1)
// required: .this.T
什么有效:
case class KN[W <: Witness.Lt[Int]](w: W) extends KnownAxis[W] {
val n = w.value
}
KN(Witness(1))
它似乎符合您问题中的要求,但我不知道它是否适用于您的其余代码。
您可能还想考虑这个在 Scala 2.13 中不需要 Shapeless 的替代方案:
trait Axis extends Serializable
case object UnknownAxis extends Axis
trait KnownAxis[W <: Int with Singleton] extends Axis {
def n: W
def ++(that: KnownAxis[W]): Unit = {}
}
case class KN[W <: Int with Singleton](n: W) extends KnownAxis[W]
KN(1)
对于 2.12
import shapeless.syntax.singleton._
...
KN(1.narrow)
尝试使用 Int
的单例子类型参数化 KnownAxis
trait KnownAxis[N <: Int] extends Axis {
def n: N
def ++(that: KnownAxis[N]): Unit = {}
}
case class K1(n: Witness.`1`.T) extends KnownAxis[Witness.`1`.T]
case class K2(n: Witness.`2`.T) extends KnownAxis[Witness.`2`.T]
case class KN[N <: Int](n: N)(implicit w: Witness.Aux[N]) extends KnownAxis[N]
import shapeless.syntax.singleton._
KN[Witness.`1`.T](1.narrow)
或者只是
KN[Witness.`1`.T](1)
或者用抽象class代替特征
abstract class KnownAxis[N <: Int](implicit w: Witness.Aux[N]) extends Axis {
def n: N
def ++(that: KnownAxis[N]): Unit = {}
}
假设我正在编写一个向量乘法程序。按照本文要求:
https://etrain.github.io/2015/05/28/type-safe-linear-algebra-in-scala
只有当两个向量的维数相等时,乘法才能编译成功。为此,我定义了一个通用类型 Axis
,它使用无形文字类型(维数)作为类型参数:
import shapeless.Witness
trait Axis extends Serializable
case object UnknownAxis extends Axis
trait KnownAxis[W <: Witness.Lt[Int]] extends Axis {
def n: Int
def ++(that: KnownAxis[W]): Unit = {}
}
object KnownAxis {
val w1 = Witness(1)
val w2 = Witness(2)
case class K1(n: Witness.`1`.T) extends KnownAxis[w1.type]
case class K2(n: Witness.`2`.T) extends KnownAxis[w2.type]
// K2(2) ++ K1(1) // doesn't compile
K2(2) ++ K2(2)
}
到目前为止一切顺利,但是当我尝试将其概括为所有 n:
时,问题出现了 case class KN[W <: Witness.Lt[Int]](n: W#T) extends KnownAxis[W]
KN(1)
以上代码触发编译错误:
Axis.scala:36: type mismatch;
found : Int(1)
required: this.T
[ERROR] KN(1)
[ERROR] ^
[ERROR] one error found
我的问题是:为什么 Spark 不能专注于更精细的类型 Witness.`1`.T
,而是使用类型 Int
?如何覆盖此行为才能成功定义 case class KN
?
更新 1: 跟进已移至新问题:
When using the singleton type feature of Scala shapeless, how to force the compiler to use narrow/singleton type as an implicit parameter?
Scala 不能在给定 W#T
的情况下推断出 W
也就不足为奇了,因为一般来说两个不同的 W
有可能具有相同的 W#T
.不适用于见证类型,但编译器不会对它们进行特殊处理。
我期望的工作(但不确定为什么不工作)是指定类型参数:
KN[Witness.`1`](1)
// error: type arguments [scala.this.Any] do not conform to method apply's type parameter bounds [W <: shapeless.this.Witness.Lt[scala.this.Int]]
或更有可能
KN[w1.type](1)
// error: type mismatch;
// found : scala.this.Int(1)
// required: .this.T
什么有效:
case class KN[W <: Witness.Lt[Int]](w: W) extends KnownAxis[W] {
val n = w.value
}
KN(Witness(1))
它似乎符合您问题中的要求,但我不知道它是否适用于您的其余代码。
您可能还想考虑这个在 Scala 2.13 中不需要 Shapeless 的替代方案:
trait Axis extends Serializable
case object UnknownAxis extends Axis
trait KnownAxis[W <: Int with Singleton] extends Axis {
def n: W
def ++(that: KnownAxis[W]): Unit = {}
}
case class KN[W <: Int with Singleton](n: W) extends KnownAxis[W]
KN(1)
对于 2.12
import shapeless.syntax.singleton._
...
KN(1.narrow)
尝试使用 Int
KnownAxis
trait KnownAxis[N <: Int] extends Axis {
def n: N
def ++(that: KnownAxis[N]): Unit = {}
}
case class K1(n: Witness.`1`.T) extends KnownAxis[Witness.`1`.T]
case class K2(n: Witness.`2`.T) extends KnownAxis[Witness.`2`.T]
case class KN[N <: Int](n: N)(implicit w: Witness.Aux[N]) extends KnownAxis[N]
import shapeless.syntax.singleton._
KN[Witness.`1`.T](1.narrow)
或者只是
KN[Witness.`1`.T](1)
或者用抽象class代替特征
abstract class KnownAxis[N <: Int](implicit w: Witness.Aux[N]) extends Axis {
def n: N
def ++(that: KnownAxis[N]): Unit = {}
}