I've been trying to map the types of an HList
from scala's shapeless
package without having access to their values.
The following succeeds in mapping the values of an 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! Apart from not being able to use implicitly[A =:= B]
for some reason, the values of an HList
have been mapped and so have their types.
Now, suppose we don't have the HList
value but we know its type. How can we map over its types?
I tried the following based on the definition of 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]
}
}
How does one get the type of a mapped HList
without having access to its value? Is there a way of debugging why the compiler can't prove something? Thankyou for reading.
In the case of TestListActualMappedType
you've got the singleton type for mappedList
, which isn't the same as the inferred type of mappedList
. You can see exactly the same issue without involving 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]
^
You could ask for evidence that x.type
is a subtype of String
or you could use shapeless.test.typed
, which would look like this in your case:
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)
This doesn't really buy you much over explicitly specifying the type, though.
You can ask for evidence that the output type of a type class like Mapper
is the type you expect for particular input types:
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>
This is more likely to be useful, but again it depends on what exactly you're trying to convince yourself of.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With