Shapeless map HList 取决于目标类型
Shapeless map HList depending on target types
我有以下问题,我想将一个 HList 的项目映射到另一个 HList,但是如果 "target" 类型是 [=16],源 HList 中的字符串应该只转换为 URL =].
val name = "Whosebug"
val url = "https://whosebug.com/q"
val list = name :: url :: HNil
val mapped: String :: URL :: HNil = list.map(???)
就我的研究而言,所有 Poly 的东西只关心输入类型而不关心输出类型。那么有什么方法可以实现我的目标吗?
我不认为你会得到你想要的,因为 Scala 的隐式解析发生在类型推断之前(但谁知道呢——人们总是在 Scala 中做一些让我惊讶的事情)。
(旁注:CanBuildFrom
/ breakOut
模式支持与您所要求的类似的东西,但我看不出有什么方法可以让它在这种情况下工作,因为源类型确实限制了什么实例可用。)
不过,对于这种情况有一个非常标准的解决方法,涉及使用助手 class 来近似类型参数的部分应用。假设您有一个相当简单的类型 class 来捕获您的转换逻辑:
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)
}
}
现在您可以尝试这样的操作:
def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
但是这里有两个问题。第一个是如果你让类型参数被推断,你总是会得到一个将每个字符串变成 URL 的转换。您可以通过显式提供两种类型参数来覆盖此行为,但是呃。
我们可以(某种程度上)通过帮助来改善这种情况 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]
现在你可以这样写了:
scala> val mapped = convert[String :: URL :: HNil](list)
mapped: shapeless.::[String,shapeless.::[java.net.URL,shapeless.HNil]] = Whosebug :: https://whosebug.com/q :: HNil
这并不完全符合您的要求,但非常接近,因为我们必须明确指定的唯一类型是所需的目标类型。
我有以下问题,我想将一个 HList 的项目映射到另一个 HList,但是如果 "target" 类型是 [=16],源 HList 中的字符串应该只转换为 URL =].
val name = "Whosebug"
val url = "https://whosebug.com/q"
val list = name :: url :: HNil
val mapped: String :: URL :: HNil = list.map(???)
就我的研究而言,所有 Poly 的东西只关心输入类型而不关心输出类型。那么有什么方法可以实现我的目标吗?
我不认为你会得到你想要的,因为 Scala 的隐式解析发生在类型推断之前(但谁知道呢——人们总是在 Scala 中做一些让我惊讶的事情)。
(旁注:CanBuildFrom
/ breakOut
模式支持与您所要求的类似的东西,但我看不出有什么方法可以让它在这种情况下工作,因为源类型确实限制了什么实例可用。)
不过,对于这种情况有一个非常标准的解决方法,涉及使用助手 class 来近似类型参数的部分应用。假设您有一个相当简单的类型 class 来捕获您的转换逻辑:
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)
}
}
现在您可以尝试这样的操作:
def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
但是这里有两个问题。第一个是如果你让类型参数被推断,你总是会得到一个将每个字符串变成 URL 的转换。您可以通过显式提供两种类型参数来覆盖此行为,但是呃。
我们可以(某种程度上)通过帮助来改善这种情况 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]
现在你可以这样写了:
scala> val mapped = convert[String :: URL :: HNil](list)
mapped: shapeless.::[String,shapeless.::[java.net.URL,shapeless.HNil]] = Whosebug :: https://whosebug.com/q :: HNil
这并不完全符合您的要求,但非常接近,因为我们必须明确指定的唯一类型是所需的目标类型。