Return 给定方法参数的最具体类型
Return most specific type given a method parameter
我试图在给定一些可变参数的情况下创建一个特定且唯一的类型。
在下面列出了代表前四个自然数的预定义类型(使用类似 HList 的方法 Digit0..Digit9、DimensionCons 和 EndDimension(如 HNil))。
object Defined {
type D1 = DimensionCons[Digit1, EndDimension.type]
val d1 = DimensionCons(Digit1(), EndDimension)
type D2 = DimensionCons[Digit2, EndDimension.type]
val d2 = DimensionCons(Digit2(), EndDimension)
type D3 = DimensionCons[Digit3, EndDimension.type]
val d3 = DimensionCons(Digit3(), EndDimension)
type D4 = DimensionCons[Digit4, EndDimension.type]
val d4 = DimensionCons(Digit4(), EndDimension)
}
我正在寻找一种带有指定签名的方法,例如
def getDimensionTpe[D <: Dimension](dim: Int) : D
例如returns DimensionCons[Digit2, EndDimension.type]
对于 getDimensionTpe(2)
。
问题:
- 如果没有白盒宏(生成类型?)这可能吗?如果是这样,如何以及需要注意什么?
- 是否有可能或适用的替代技术?
- 路径依赖类型有帮助吗?
谢谢,马丁
我不确定我是否理解您对 DimensionCons
和 DigitN
业务到底做了什么,但是可以使用 Shapeless 编写一个采用整数的方法(尽管只是一个文字常量——它必须在编译时已知)并让该整数确定 return 值的静态类型。
为了简化代码以获得完整的工作示例,假设我们要编写如下方法:
trait Dimension
case class Dim1() extends Dimension
case class Dim2() extends Dimension
case class Dim3() extends Dimension
// And so on...
def getDimensionTpe[D <: Dimension](dim: Int) : D
…其中 getDimensionTpe(1)
将 return Dim1()
(静态类型为 Dim1
),getDimensionTpe(2)
将 return Dim2()
等。为此,我们可以在 Shapeless 自然数和维度之间引入类型 class 映射:
import shapeless.{ DepFn0, Nat }
trait DimMap[N <: Nat] extends DepFn0 {
type Out <: Dimension
}
object DimMap {
type Aux[N <: Nat, D <: Dimension] = DimMap[N] { type Out = D }
implicit val dimMap1: Aux[Nat._1, Dim1] = new DimMap[Nat._1] {
type Out = Dim1
def apply(): Dim1 = Dim1()
}
implicit val dimMap2: Aux[Nat._2, Dim2] = new DimMap[Nat._2] {
type Out = Dim2
def apply(): Dim2 = Dim2()
}
implicit val dimMap3: Aux[Nat._3, Dim3] = new DimMap[Nat._3] {
type Out = Dim3
def apply(): Dim3 = Dim3()
}
// And so on as needed.
}
如果您的维度类型有更多结构,则可以避免此处的样板文件,但我不清楚您的 DigitN
内容是如何工作的。不过,这似乎与问题的要点正交,关于如何定义 getDimensionTpe
.
如果您不介意编写一个白盒宏,您可以将其定义为具有如下签名的内容:
def getDimensionTpe[D <: Dimension](dim: Int) : D
不过,这是一个巨大的麻烦,而 Shapeless 允许您编写本质上相同的东西,而无需自定义宏。鉴于上面的 DimMap
类型 class,您可以编写以下内容:
def getDimensionTpe(dim: Nat)(implicit m: DimMap[dim.N]) : m.Out = m()
然后:
scala> val d1: Dim1 = getDimensionTpe(1)
d1: Dim1 = Dim1()
scala> val d2: Dim2 = getDimensionTpe(2)
d2: Dim2 = Dim2()
scala> val d3: Dim3 = getDimensionTpe(3)
d3: Dim3 = Dim3()
如果你把静态类型搞错了,编译器会告诉你:
scala> val d3: Dim1 = getDimensionTpe(3)
<console>:15: error: type mismatch;
found : DimMap.dimMap3.Out
(which expands to) Dim3
required: Dim1
val d3: Dim1 = getDimensionTpe(3)
^
如果你提供了一个没有映射的整型字面量,这也是一个编译时错误:
scala> getDimensionTpe(0)
<console>:14: error: could not find implicit value for parameter m: DimMap[shapeless._0]
getDimensionTpe(0)
^
最后,如果您提供的 Int
参数不是整数文字,您也会收到编译器错误:
scala> val x = 1
x: Int = 1
scala> getDimensionTpe(x)
<console>:16: error: Expression x does not evaluate to a non-negative Int literal
getDimensionTpe(x)
^
在幕后,Shapeless 正在使用一个宏来实现这一点,它甚至 都不可怕——您可以阅读 the NatMacros
definition 了解详细信息。
我试图在给定一些可变参数的情况下创建一个特定且唯一的类型。
在下面列出了代表前四个自然数的预定义类型(使用类似 HList 的方法 Digit0..Digit9、DimensionCons 和 EndDimension(如 HNil))。
object Defined {
type D1 = DimensionCons[Digit1, EndDimension.type]
val d1 = DimensionCons(Digit1(), EndDimension)
type D2 = DimensionCons[Digit2, EndDimension.type]
val d2 = DimensionCons(Digit2(), EndDimension)
type D3 = DimensionCons[Digit3, EndDimension.type]
val d3 = DimensionCons(Digit3(), EndDimension)
type D4 = DimensionCons[Digit4, EndDimension.type]
val d4 = DimensionCons(Digit4(), EndDimension)
}
我正在寻找一种带有指定签名的方法,例如
def getDimensionTpe[D <: Dimension](dim: Int) : D
例如returns DimensionCons[Digit2, EndDimension.type]
对于 getDimensionTpe(2)
。
问题:
- 如果没有白盒宏(生成类型?)这可能吗?如果是这样,如何以及需要注意什么?
- 是否有可能或适用的替代技术?
- 路径依赖类型有帮助吗?
谢谢,马丁
我不确定我是否理解您对 DimensionCons
和 DigitN
业务到底做了什么,但是可以使用 Shapeless 编写一个采用整数的方法(尽管只是一个文字常量——它必须在编译时已知)并让该整数确定 return 值的静态类型。
为了简化代码以获得完整的工作示例,假设我们要编写如下方法:
trait Dimension
case class Dim1() extends Dimension
case class Dim2() extends Dimension
case class Dim3() extends Dimension
// And so on...
def getDimensionTpe[D <: Dimension](dim: Int) : D
…其中 getDimensionTpe(1)
将 return Dim1()
(静态类型为 Dim1
),getDimensionTpe(2)
将 return Dim2()
等。为此,我们可以在 Shapeless 自然数和维度之间引入类型 class 映射:
import shapeless.{ DepFn0, Nat }
trait DimMap[N <: Nat] extends DepFn0 {
type Out <: Dimension
}
object DimMap {
type Aux[N <: Nat, D <: Dimension] = DimMap[N] { type Out = D }
implicit val dimMap1: Aux[Nat._1, Dim1] = new DimMap[Nat._1] {
type Out = Dim1
def apply(): Dim1 = Dim1()
}
implicit val dimMap2: Aux[Nat._2, Dim2] = new DimMap[Nat._2] {
type Out = Dim2
def apply(): Dim2 = Dim2()
}
implicit val dimMap3: Aux[Nat._3, Dim3] = new DimMap[Nat._3] {
type Out = Dim3
def apply(): Dim3 = Dim3()
}
// And so on as needed.
}
如果您的维度类型有更多结构,则可以避免此处的样板文件,但我不清楚您的 DigitN
内容是如何工作的。不过,这似乎与问题的要点正交,关于如何定义 getDimensionTpe
.
如果您不介意编写一个白盒宏,您可以将其定义为具有如下签名的内容:
def getDimensionTpe[D <: Dimension](dim: Int) : D
不过,这是一个巨大的麻烦,而 Shapeless 允许您编写本质上相同的东西,而无需自定义宏。鉴于上面的 DimMap
类型 class,您可以编写以下内容:
def getDimensionTpe(dim: Nat)(implicit m: DimMap[dim.N]) : m.Out = m()
然后:
scala> val d1: Dim1 = getDimensionTpe(1)
d1: Dim1 = Dim1()
scala> val d2: Dim2 = getDimensionTpe(2)
d2: Dim2 = Dim2()
scala> val d3: Dim3 = getDimensionTpe(3)
d3: Dim3 = Dim3()
如果你把静态类型搞错了,编译器会告诉你:
scala> val d3: Dim1 = getDimensionTpe(3)
<console>:15: error: type mismatch;
found : DimMap.dimMap3.Out
(which expands to) Dim3
required: Dim1
val d3: Dim1 = getDimensionTpe(3)
^
如果你提供了一个没有映射的整型字面量,这也是一个编译时错误:
scala> getDimensionTpe(0)
<console>:14: error: could not find implicit value for parameter m: DimMap[shapeless._0]
getDimensionTpe(0)
^
最后,如果您提供的 Int
参数不是整数文字,您也会收到编译器错误:
scala> val x = 1
x: Int = 1
scala> getDimensionTpe(x)
<console>:16: error: Expression x does not evaluate to a non-negative Int literal
getDimensionTpe(x)
^
在幕后,Shapeless 正在使用一个宏来实现这一点,它甚至 都不可怕——您可以阅读 the NatMacros
definition 了解详细信息。