2016-10-02 13 views
4

scaler의 shapeless 패키지에서 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] 
    } 

} 

쿨 HList의 값을 매핑에 성공! 어떤 이유로 든 implicitly[A =:= B]을 사용할 수 없다는 것 외에도 HList의 값이 매핑되어 유형이 있습니다.

이제 우리는 HList 값이 없지만 그 유형을 알고 있다고 가정합니다. 유형을 어떻게 매핑 할 수 있습니까?

나는 시도 map here의 정의에 따라 다음

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] 
    } 

} 

어떻게 하나의 값에 액세스 할 필요없이 매핑 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

감사합니다. -이게 정말 도움이됩니다. 두 번째 코드 블록에서 컴파일 할 때'm'은 어떻게됩니까? 최적화되어 있습니까? 'm.Out'가 실제로 값을 만들지 않고 가질 수있는 타입을 얻는 방법이 있습니까? – user1158559