映射非形状HList的类型



我一直在尝试从scala的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]
  }
}

酷!除了由于某种原因不能使用implicitly[A =:= B]之外,HList的值已经被映射,因此具有它们的类型。

现在,假设我们没有HList值,但我们知道它的类型。我们如何映射它的类型?

我尝试了以下基于map的定义在这里:

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的类型?有没有一种调试的方法,为什么编译器不能证明什么?谢谢你的阅读。

TestListActualMappedType的情况下,您已经获得了mappedList的单例类型,这与mappedList的推断类型不同。你可以看到完全相同的问题,不涉及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]
                 ^

你可以要求证据证明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]]} = shapeless.ops.hlist$Mapper$$anon$5@6f3598cd
scala> implicitly[m.Out =:= (String :: Int :: HNil)]
res1: =:=[m.Out,shapeless.::[String,shapeless.::[Int,shapeless.HNil]]] = <function1>

这可能更有用,但这取决于你到底想说服自己什么

最新更新