如何使用一元类型构造函数推断 Shapeless 记录值的内部类型?
How to infer inner type of Shapeless record value with unary type constructor?
我无法理解 Shapeless 记录选择器与 Scala 类型推断交互的方式。我正在尝试创建一种方法,该方法可以通过键从无形记录中获取字段,前提是该字段的值具有特定的一元类型构造函数,在这种特殊情况下 Vector[_]
,然后获取内部值Vector
中的推断类型 V
,在本例中为 Vector.apply()
.
我觉得我很亲近。这有效,具体的内部类型为 Int
:
val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil
def getIntFromVectorField[L <: HList](l: L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L, fieldName.T, Vector[Int]]
):Int = l(fieldName).apply(index)
getIntFromVectorField(record,"a",1) // Returns 1
getIntFromVectorField(record,"b",0) // Does not compile, as intended
但是如果我尝试推断内部类型,它会失败:
def getValueFromVectorField[L <: HList,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,Vector[V]]
):V = l(fieldName).apply(index) // Compiles
getValueFromVectorField(record,"a",1) // Why does this not compile?
这是完整的错误:
could not find implicit value for parameter sel:
shapeless.ops.record.Selector[shapeless.::[scala.collection.immutable.Vector[Int]
with shapeless.labelled.KeyTag[String("a"),scala.collection.immutable.Vector[Int]],
shapeless.::[scala.collection.immutable.Set[Int]
with shapeless.labelled.KeyTag[String("b"),scala.collection.immutable.Set[Int]],
shapeless.HNil]],String("a")]{type Out = scala.collection.immutable.Vector[V]}
我能做的是:
def getValueFromVectorField[L <: HList,T,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,T],
unpack: Unpack1[T,Vector,V]
):V = l(fieldName) match {
case v:Vector[V] => v.apply(index)
}
getValueFromVectorField(record,"a",1) // Returns 1, Yay!
getValueFromVectorField(record,"b",0) // Does not compile, as intended
哪个应该是安全的,是吗?但是模式匹配对于 shapeless 来说并不是很惯用,我想知道为什么更简洁的推理方法不起作用。有更简洁的方法吗?
Scala 在这种情况下的类型推断真的很糟糕(你想统一函数依赖的结果和类似 Vector[V]
的结果并推断出 V
)。
您可以通过分解以下步骤来帮助编译器完成整个过程:
import shapeless._, ops.record.Selector, syntax.singleton._
def getValueFromVectorField[L <: HList, VS, V](
l: L,
fieldName: Witness,
index: Int
)(implicit
sel: Selector.Aux[L, fieldName.T, VS],
ev: VS <:< Vector[V]
): V = sel(l).apply(index)
val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil
getValueFromVectorField(record,"a",1) // Returns 1, Yay!
getValueFromVectorField(record,"b",0) // Does not compile, as intended
现在它会先推断 VS
,然后找出 VS
是 Vector[V]
的子类型,而不必一步完成。
这与您的 Unpack1
版本所做的完全相同,只是 Unpack1
仅证明 T
是 Vector[V]
——它实际上并没有给您一种从 T
获取 Vector[V]
的方法(不像 <:<
,它确实如此)。
所以你的 Unpack1
版本是安全的,因为你可以说服自己它提供了你需要的所有证据,但它们不是编译器理解的形式,所以你有与模式匹配中的类型大小写向下转换。 <:<
更好,因为编译器确实理解它,但也因为它作为此限制的解决方法更容易被识别,因为它由标准库等提供。
我无法理解 Shapeless 记录选择器与 Scala 类型推断交互的方式。我正在尝试创建一种方法,该方法可以通过键从无形记录中获取字段,前提是该字段的值具有特定的一元类型构造函数,在这种特殊情况下 Vector[_]
,然后获取内部值Vector
中的推断类型 V
,在本例中为 Vector.apply()
.
我觉得我很亲近。这有效,具体的内部类型为 Int
:
val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil
def getIntFromVectorField[L <: HList](l: L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L, fieldName.T, Vector[Int]]
):Int = l(fieldName).apply(index)
getIntFromVectorField(record,"a",1) // Returns 1
getIntFromVectorField(record,"b",0) // Does not compile, as intended
但是如果我尝试推断内部类型,它会失败:
def getValueFromVectorField[L <: HList,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,Vector[V]]
):V = l(fieldName).apply(index) // Compiles
getValueFromVectorField(record,"a",1) // Why does this not compile?
这是完整的错误:
could not find implicit value for parameter sel:
shapeless.ops.record.Selector[shapeless.::[scala.collection.immutable.Vector[Int]
with shapeless.labelled.KeyTag[String("a"),scala.collection.immutable.Vector[Int]],
shapeless.::[scala.collection.immutable.Set[Int]
with shapeless.labelled.KeyTag[String("b"),scala.collection.immutable.Set[Int]],
shapeless.HNil]],String("a")]{type Out = scala.collection.immutable.Vector[V]}
我能做的是:
def getValueFromVectorField[L <: HList,T,V](l:L, fieldName:Witness, index:Int)(implicit
sel: Selector.Aux[L,fieldName.T,T],
unpack: Unpack1[T,Vector,V]
):V = l(fieldName) match {
case v:Vector[V] => v.apply(index)
}
getValueFromVectorField(record,"a",1) // Returns 1, Yay!
getValueFromVectorField(record,"b",0) // Does not compile, as intended
哪个应该是安全的,是吗?但是模式匹配对于 shapeless 来说并不是很惯用,我想知道为什么更简洁的推理方法不起作用。有更简洁的方法吗?
Scala 在这种情况下的类型推断真的很糟糕(你想统一函数依赖的结果和类似 Vector[V]
的结果并推断出 V
)。
您可以通过分解以下步骤来帮助编译器完成整个过程:
import shapeless._, ops.record.Selector, syntax.singleton._
def getValueFromVectorField[L <: HList, VS, V](
l: L,
fieldName: Witness,
index: Int
)(implicit
sel: Selector.Aux[L, fieldName.T, VS],
ev: VS <:< Vector[V]
): V = sel(l).apply(index)
val record = ( "a" ->> Vector(0,2,4) ) :: ( "b" ->> Set(1,3,5) ) :: HNil
getValueFromVectorField(record,"a",1) // Returns 1, Yay!
getValueFromVectorField(record,"b",0) // Does not compile, as intended
现在它会先推断 VS
,然后找出 VS
是 Vector[V]
的子类型,而不必一步完成。
这与您的 Unpack1
版本所做的完全相同,只是 Unpack1
仅证明 T
是 Vector[V]
——它实际上并没有给您一种从 T
获取 Vector[V]
的方法(不像 <:<
,它确实如此)。
所以你的 Unpack1
版本是安全的,因为你可以说服自己它提供了你需要的所有证据,但它们不是编译器理解的形式,所以你有与模式匹配中的类型大小写向下转换。 <:<
更好,因为编译器确实理解它,但也因为它作为此限制的解决方法更容易被识别,因为它由标准库等提供。