HList lifeless map depending on target types

I have the following problem: I want to map the HList elements to another HList, but the lines in the source HList should only be converted to URLs if the type โ€œtargetโ€ is a URL.

val name = "Stackoverflow" val url = "https://stackoverflow.com/q" val list = name :: url :: HNil val mapped: String :: URL :: HNil = list.map(???) 

To my knowledge, all Poly materials only care about the type of input, but not the type of output. So, are there ways to archive my goal?

+7
scala shapeless
source share
1 answer

I donโ€™t think that you will get exactly what you want, since Scala implicit resolution happens before type input (but who knows - people always amaze me in Scala).

(Side note: CanBuildFrom / breakOut supports something similar to what you are asking for, but I see no way to make it work in this situation, since the type of source limits the availability of instances.)

There is a fairly standard workaround for this kind of situation, although it involves using an auxiliary class to approximate the partial use of type parameters. Suppose you have a fairly simple type class that captures your conversion logic:

 import java.net.URL import shapeless._ trait Convert[I <: HList, O <: HList] { def apply(i: I): O } object Convert extends LowPriorityConvertInstances { implicit val convertHNil: Convert[HNil, HNil] = new Convert[HNil, HNil] { def apply(i: HNil): HNil = i } implicit def convertHConsURL[T <: HList, TO <: HList](implicit c: Convert[T, TO] ): Convert[String :: T, URL :: TO] = new Convert[String :: T, URL :: TO] { def apply(i: String :: T): URL :: TO = new URL(i.head) :: c(i.tail) } } sealed class LowPriorityConvertInstances { implicit def convertHCons[H, T <: HList, TO <: HList](implicit c: Convert[T, TO] ): Convert[H :: T, H :: TO] = new Convert[H :: T, H :: TO] { def apply(i: H :: T): H :: TO = i.head :: c(i.tail) } } 

Now you can try something like this:

 def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i) 

But there are two problems. First, if you specify type parameters, you will always get a conversion that turns each line into a URL. You can override this behavior by explicitly providing both type parameters, but ugh.

We can (sort of) improve this situation with a helper class:

 class PartiallyAppliedConvert[O <: HList] { def apply[I <: HList](i: I)(implicit c: Convert[I, O]): O = c(i) } def convert[O <: HList]: PartiallyAppliedConvert[O] = new PartiallyAppliedConvert[O] 

Now you can write the following:

 scala> val mapped = convert[String :: URL :: HNil](list) mapped: shapeless.::[String,shapeless.::[java.net.URL,shapeless.HNil]] = Stackoverflow :: https://stackoverflow.com/q :: HNil 

This is not quite what you requested, but it is pretty close, since the only type we need to explicitly indicate is the desired type of target.

+8
source share

All Articles