如何使用一元类型构造函数推断无形状记录值的内部类型? [英] How to infer inner type of Shapeless record value with unary type constructor?

查看:53
本文介绍了如何使用一元类型构造函数推断无形状记录值的内部类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解 Shapeless 记录选择器与 Scala 的类型推断交互的方式.我正在尝试创建一种方法,该方法可以通过键从 Shapeless 记录中获取字段,仅当该字段的值具有特定的一元类型构造函数时,在这种特殊情况下 Vector[_],然后从 Vector 中获取推断类型 V 的内部值,在本例中使用 Vector.apply().

I'm having trouble understanding the way the Shapeless record Selector interacts with scala's type inference. I'm trying to create a method that can grab a field from a Shapeless record by key, only if the value of the field has a certain unary type constructor, in this particular case Vector[_], and then grab an inner value of inferred type V out of the Vector, in this case with Vector.apply().

我觉得我很接近.这是有效的,具有 Int 的具体内部类型:

I feel like I'm close. This works, with a concrete inner type of 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

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

But if I try to infer the inner type, it fails:

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

哪个应该是安全的,是吗?但是模式匹配对于无形来说并不是很惯用,我想知道为什么更简洁的推理方法不起作用.有没有更干净的方法来做到这一点?

Which should be safe, yes? But the pattern matching doesn't feel very idiomatic for shapeless, and I'm wondering why the more concise approach with inference doesn't work. Is there a cleaner way to do this?

推荐答案

Scala 在这种情况下(你想统一函数依赖的结果和类似 Vector[V] 并推断出 V).

Scala is really bad about type inference in cases like this (where you want to unify the result of a functional dependency and something like Vector[V] and have the V inferred).

您可以通过分解步骤来帮助编译器完成整个过程:

You can help the compiler through the process by breaking down the steps:

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 然后找出 VSVector[V] 的子类型,而不是必须一步完成.

Now it'll infer VS first and then figure out that VS is a subtype of Vector[V], instead of having to do both in one step.

这和你的 Unpack1 版本所做的完全一样,除了 Unpack1 只证明 TVector[V]——它实际上并没有给你一种从 T 获取 Vector[V] 的方法(不像 <:<,确实如此).

This is exactly the same thing that your Unpack1 version does, except that Unpack1 only proves that T is Vector[V]—it doesn't actually give you a way to get a Vector[V] from a T (unlike <:<, which does).

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

So your Unpack1 version is safe, in the sense that you can convince yourself that it provides all the pieces of evidence you need, but they're not in a form the compiler understands, so you have to downcast with the type case in the pattern match. <:< is better because the compiler does understand it, but also because it's more recognizable as a workaround for this limitation, since it's provided by the standard library, etc.

这篇关于如何使用一元类型构造函数推断无形状记录值的内部类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆