如何用一元类型构造函数推断Shapeless记录值的内部类型?

Ric*_*ing 5 scala type-inference shapeless

我无法理解Shapeless记录选择器与scala类型推断交互的方式.我试图创建一个可以通过按键将未成形的纪录抢实地,只有当字段的值具有一定的一元型构造,在这种特殊情况下的方法Vector[_],然后抢推断类型的内在价值V出来的的Vector,在这种情况下有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
Run Code Online (Sandbox Code Playgroud)

但如果我试图推断内部类型,它会失败:

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? 
Run Code Online (Sandbox Code Playgroud)

这是完整的错误:

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]}
Run Code Online (Sandbox Code Playgroud)

我能做的是这样的:

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
Run Code Online (Sandbox Code Playgroud)

哪个应该是安全的,是吗?但模式匹配对于无形而言并不是非常惯用,我想知道为什么更简洁的推理方法不起作用.有更清洁的方法吗?

Tra*_*own 6

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
Run Code Online (Sandbox Code Playgroud)

现在它VS首先推断,然后弄清楚这VS是一个子类型Vector[V],而不是一步完成两个.

这与你的Unpack1版本完全相同,除了Unpack1只证明TVector[V]-it实际上没有给你一个Vector[V]从a 获得a 的方法T(不像<:<,哪个).

因此,您的Unpack1版本是安全的,因为您可以说服自己它提供了您需要的所有证据,但它们不是编译器理解的形式,因此您必须在模式匹配中使用类型情况进行向下转换.<:<更好,因为编译器确实理解它,但也因为它更容易被识别为此限制的解决方法,因为它是由标准库提供的,等等.