2016-10-02 8 views
4

私は、scalaのshapelessパッケージからHListの型をその値にアクセスすることなくマップしようとしました。シェイプレスなHListの型をマップする

以下はHList

import shapeless._ 
import shapeless.Poly._ 
import ops.hlist.Mapper 
import ops.hlist.Mapper._ 

trait Person { 
    type Value 
    val v : Value 
} 

case class StringPerson extends Person { 
    type Value = String 
    val v = "I like strings" 
} 

case class IntPerson extends Person { 
    type Value = Int 
    val v = 42 
} 

object what_is_going_on { 

    object test_value_op { 
    val stringPerson = StringPerson() 
    val intPerson = IntPerson() 

    trait lpvfun extends Poly1 { 
     implicit def default[A <: Person] = at[A](_.v) 
    } 

    object vfun extends lpvfun {} 

    // Use these to generate compiler errors if the mapped type is not what we'd expect: 

    type TestListType = StringPerson :: IntPerson :: HNil 
    type TestListExpectedMappedType = String :: Int :: HNil 

    // Input: 
    val testList : TestListType = stringPerson :: intPerson :: HNil 

    // Output: 
    val mappedList : TestListExpectedMappedType = testList map vfun 

    // Get the actual mapped type 
    type TestListActualMappedType = mappedList.type 

    // This compiles...... 
    val mappedList1 : TestListActualMappedType = mappedList 

    // .... but weirdly this line doesn't. That isn't the point of this question, but I'd be very grateful for an answer. 
    //implicitly[TestListActualMappedType =:= TestListExpectedMappedType] 
    } 

} 

クールの値をマッピングすることに成功しました!なんらかの理由でimplicitly[A =:= B]を使用できないことを別にすれば、HListの値がマップされているため、その型もあります。

ここで、HListという値はありませんが、そのタイプはわかっています。どのように型をマップできますか?

私はmaphereの定義に基づいて、次を試してみました:

object test_type_op { 
    type TestListType = StringPerson :: IntPerson :: HNil 
    type TestListExpectedMappedType = String :: Int :: HNil 

    // Attempt 1 does not work, compiler cannot prove =:= 
    type MappedType = Mapper[vfun.type, TestListType]#Out 
    implicitly[MappedType =:= TestListExpectedMappedType] 

    // Attempt 2 does not work, compiler cannot prove =:= 
    class GetMapper { 
    implicit val mapper : Mapper[vfun.type, TestListType] 
    implicitly[mapper.Out =:= TestListExpectedMappedType] 
    } 

} 

どのように1は、その値にアクセスすることなく、マップされたHListの種類を取得していますか?コンパイラが何かを証明できない理由をデバッグする方法はありますか?読んでくれてありがとう。

答えて

2

TestListActualMappedTypeの場合は、mappedListのシングルトンタイプがあります。これは、推測タイプmappedListと同じではありません。

scala> val x = "foo" 
x: String = foo 

scala> implicitly[x.type =:= String] 
<console>:13: error: Cannot prove that x.type =:= String. 
     implicitly[x.type =:= String] 
       ^

あなたはx.typeStringのサブタイプであるという証拠を求めることができるか、あなたの場合には、このようになりますshapeless.test.typed、使用することができます:あなたは型崩れを伴うことなく、正確に同じ問題を見ることができます

import shapeless._, ops.hlist.Mapper 

trait Person { 
    type Value 
    val v : Value 
} 

case class StringPerson() extends Person { 
    type Value = String 
    val v = "I like strings" 
} 

case class IntPerson() extends Person { 
    type Value = Int 
    val v = 42 
} 

trait lpvfun extends Poly1 { 
    implicit def default[A <: Person] = at[A](_.v) 
} 

object vfun extends lpvfun {} 

val stringPerson = StringPerson() 
val intPerson = IntPerson() 

val testList = stringPerson :: intPerson :: HNil 
val mappedList = testList map vfun 

shapeless.test.typed[String :: Int :: HNil](mappedList) 

これは実際にあなたがタイプを明示的に指定することよりもはるかにあなたを買うわけではありません。

あなたはMapperのような型クラスの出力タイプを使用すると、特定の入力タイプに対して期待するタイプであることの証拠を求めることができます:

scala> val m = Mapper[vfun.type, StringPerson :: IntPerson :: HNil] 
m: shapeless.ops.hlist.Mapper[vfun.type,shapeless.::[StringPerson,shapeless.::[IntPerson,shapeless.HNil]]]{type Out = shapeless.::[String,shapeless.::[Int,shapeless.HNil]]} = [email protected] 

scala> implicitly[m.Out =:= (String :: Int :: HNil)] 
res1: =:=[m.Out,shapeless.::[String,shapeless.::[Int,shapeless.HNil]]] = <function1> 

、これは有用である可能性が高いが、やはりそれは何に依存しますまさにあなた自身を説得しようとしています。

+0

ありがとうございます - これは本当に役立ちます。 2番目のコードブロックでは、コンパイル時に 'm'はどうなりますか?それは最適化されていますか? 'm.Out'が実際に値を作成せずに持つ型を得る方法はありますか? – user1158559

関連する問題