2016-10-02 1 views
4

J'ai essayé de mapper les types d'un HList du paquet shapeless de scala sans avoir accès à leurs valeurs.Mapper les types d'un HList sans forme

qui suit parvient à cartographier les valeurs d'un 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] 
    } 

} 

Cool! En plus de ne pas pouvoir utiliser implicitly[A =:= B] pour une raison quelconque, les valeurs d'un HList ont été mappées et ont donc leurs types. Maintenant, supposons que nous n'ayons pas la valeur HList mais nous connaissons son type. Comment pouvons-nous cartographier sur ses types?

J'ai essayé les éléments suivants en fonction de la définition de 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] 
    } 

} 

Comment peut-on obtenir le type d'un tracé HList sans avoir accès à sa valeur? Y at-il un moyen de déboguer pourquoi le compilateur ne peut pas prouver quelque chose? Merci pour la lecture.

Répondre

2

Dans le cas de TestListActualMappedType vous avez le type singleton pour mappedList, ce qui n'est pas la même chose que le type inféré de mappedList. Vous pouvez voir exactement la même question sans impliquer Shapeless:

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

Vous pouvez demander des preuves que x.type est un sous-type de String ou vous pouvez utiliser shapeless.test.typed, qui ressemblerait à ceci dans votre cas:

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) 

Cependant, cela ne vous achèterait pas vraiment plus que de spécifier explicitement le type.

Vous pouvez demander des preuves que le type de sortie d'une classe de type comme Mapper est le type que vous attendez pour les types d'entrée particuliers:

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> 

Ceci est plus susceptible d'être utile, mais encore une fois cela dépend de ce que exactement vous essayez de vous convaincre de.

+0

Merci, cela aide vraiment. Dans votre deuxième bloc de code, qu'advient-il de 'm' au moment de la compilation? Est-ce optimisé? Est-il possible d'obtenir le type que 'm.Out' aurait sans créer de valeurs? – user1158559