无形地图HList取决于目标类型

shi*_*im_ 7 scala shapeless

我有以下问题,我想将HList的项目映射到另一个HList,但如果"目标"类型是URL,则源HList中的字符串应仅转换为URL.

val name = "Stackoverflow"
val url = "https://stackoverflow.com/q"
val list = name :: url :: HNil

val mapped: String :: URL :: HNil = list.map(???)
Run Code Online (Sandbox Code Playgroud)

据我所知,我所有的Poly材料只关心输入类型,而不关心输出类型.那么有没有办法存档我的目标?

Tra*_*own 8

我不认为你会得到你想要的东西,因为Scala的隐式解决方案发生在类型推断之前(但谁知道 - 人们总是在Scala中做令我惊讶的事情).

(旁注:CanBuildFrom/ breakOutpattern支持类似于你要求的东西,但我没有看到在这种情况下使它工作的方法,因为源类型确实限制了可用的实例.)

但是,对于这种情况,有一个非常标准的解决方法,涉及使用辅助类来近似部分应用类型参数.假设您有一个相当简单的类型类来捕获转换逻辑:

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以尝试这样的事情:

def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
Run Code Online (Sandbox Code Playgroud)

但这里有两个问题.首先,如果您推断出类型参数,您将始终获得将每个字符串转换为URL的转换.您可以通过显式提供两个类型参数来覆盖此行为,但是呃.

我们可以(通过辅助类)改善这种情况:

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]
Run Code Online (Sandbox Code Playgroud)

现在您可以编写以下内容:

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

这不完全是你要求的,但它非常接近,因为我们必须明确指定的唯一类型是所需的目标类型.