【问题标题】:Scala shapeless: derive type from MapperScala 无形:从 Mapper 派生类型
【发布时间】:2015-10-20 13:58:32
【问题描述】:

doesNotCompile 方法仅接受仅包含 Label[A] 条目的 HList。有一个 Mapper 可以将 Label[A] 转换为 String(准确地说:Const[String]#λ)。 但是,当我应用映射器时,返回类型是 ev1.Out。我知道这实际上是一个只有字符串的 HList,但是我怎样才能说服编译器呢?

import shapeless._
import shapeless.poly._
import shapeless.ops.hlist._
import shapeless.UnaryTCConstraint._

object Util {
  case class Label[A](name: String, value: A)

  object GetLabelName extends (Label ~> Const[String]#λ) {
    def apply[A](label: Label[A]) = label.name
  }
}

object Main {
  import Util._

  def bar(l: List[String]) = ???

  def compiles = {
    val names = "a" :: "b" :: HNil
    bar(names.toList)
  }

  // A is an HList whose members are all Label[_]
  def doesNotCompile[A <: HList : *->*[Label]#λ](labels: A)(
    implicit ev1: Mapper[GetLabelName.type, A]) = {
  // implicit ev1: Mapper[String, A]) = {

    val names = labels map GetLabelName
    // names is of type `ev1.Out` - I want it to be an HList of Strings

    bar(names.toList)
    // error: could not find implicit value for parameter toTraversableAux:
    // shapeless.ops.hlist.ToTraversable.Aux[ev1.Out,List,Lub]
  }
}

这是完整的要点,包括。 build.sbt - 下载并运行 sbt compile: https://gist.github.com/mpollmeier/6c53e375d88a32016250

【问题讨论】:

    标签: scala shapeless implicits


    【解决方案1】:

    正如错误所说,您缺少ToTraversable 的类型类实例,没有它您无法调用names.toList。我们可以通过添加一个隐式参数来解决这个问题,但首先我们需要解决GetLabelName 的一个问题,因为它在映射到HList 时无法使用:

    scala> GetLabelName(Label("a", 5))
    res3: String = a
    
    scala> (Label("a", 5) :: HNil) map GetLabelName
    java.lang.AbstractMethodError: GetLabelName$.caseUniv()Lshapeless/PolyDefns$Case;
      ... 43 elided
    

    一种解决方案是创建一个扩展Poly1 的新多态函数:

    object getLabelName extends Poly1 {
      implicit def caseLabel[T] = at[Label[T]](_.name)
    }
    

    现在我们可以将隐式参数添加到函数中:

    def bar(l: List[String]) = l
    
    def labelNames[L <: HList : *->*[Label]#λ, M <: HList](labels: L)(implicit
      mapper: Mapper.Aux[getLabelName.type, L, M],
      trav: ToTraversable.Aux[M, List, String]
    ): List[String] = bar(labels.map(getLabelName).toList)
    

    可以用作:

    val labels = Label("a", 5) :: Label("b", 1d) :: Label("c", None) :: HNil
    labelNames(labels) // List[String] = List(a, b, c)
    

    【讨论】:

    • 太棒了,谢谢。注意:我不必创建额外的 Poly1,我只需要更改额外的 ToTraversable。
    猜你喜欢
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多