2016-10-28 13 views
5

私はShapelessレコードセレクタがスカラーの型推論とやりとりする方法を理解するのに困っています。私はフィールドの値が特定の単項型のコンストラクタを持っている場合にのみ、キーでShapelessレコードからフィールドを取得できるメソッドを作成しようとしています。Vector[_]、次に推測型の内部値を取得しますVVectorのうち、この場合はVector.apply()です。単項式のコンストラクタを使用してShapelessレコード値の内部型を推測するにはどうすればよいですか?

私は近くにいるように感じます。これは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 

どちらが安全なはずですか?しかし、パターンマッチングは、形のない人にとっては非常に慣れていません。推論ではより簡潔なアプローチが機能しないのはなぜかと思います。これを行うためのよりクリーンな方法がありますか?

答えて

6

スカラは、このような場合(関数依存性の結果を統一し、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のみTが-itが実際にあなたに(<:<とは異なり、どのません)TからVector[V]を取得する方法を与えるものではありませんVector[V]であることを証明している以外、あなたのUnpack1バージョンは、と同じことです。

Unpack1バージョンは安全です。あなたが必要とするすべての証拠を提供していると確信することができますが、コンパイラが理解できる形ではないため、タイプをダウンキャストする必要がありますパターンマッチの場合。 <:<は、コンパイラが理解しているために優れていますが、標準ライブラリなどで提供されているため、この制限の回避策として認識しやすくなっています。