Shapeless HList在foldRight之后的映射

4
一种类型级别的foldRight是可以正常工作的(getLabelWithValues),并且随后的类型级别map(getValues)也可以正常工作。但如果我将它们合并在一个方法中(getValuesFull),就不能再正常工作了。缺少什么?完整的源代码(使用sbt准备好隐式调试输出)在这里:https://github.com/mpollmeier/shapeless-playground/tree/8170a5b
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)

val label1 = Label[Int]("a")
val labels = label1 :: HNil

object combineLabelWithValue extends Poly2 {
  implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
    case (label, (acc, values)) ⇒
      (LabelWithValue(label, values(label.name).asInstanceOf[A]) :: acc, values)
  }
}

object GetLabelValue extends (LabelWithValue ~> Id) {
  def apply[B](labelWithValue: LabelWithValue[B]) = labelWithValue.value
}

val labelsWithValues: LabelWithValue[Int] :: HNil = getLabelWithValues(labels)
// manually mapping it works fine:
val valuesManual: Int :: HNil = labelsWithValues.map(GetLabelValue)

// using a second function with Mapper works fine:
val valuesSecondFn: Int :: HNil = getValues(labelsWithValues)

// error: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper.Aux[Main.GetLabelValue.type,WithValues,Values]
// val valuesFull: Int :: HNil = getValuesFull(labels)


def getLabelWithValues[L <: HList, P, WithValues](labels: L)(
  implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
  ic: IsComposite.Aux[P, WithValues, _]
): WithValues = {
  val state = Map("a" -> 5, "b" -> "five")
  val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
  ic.head(resultTuple)
}

def getValues[WithValues <: HList, Values <: HList](withValues: WithValues)(
  implicit mapper: Mapper.Aux[GetLabelValue.type, WithValues, Values]
): Values = {
  withValues.map(GetLabelValue)
}

def getValuesFull[L <: HList, P, WithValues <: HList, Values <: HList](labels: L)(
  implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
  ic: IsComposite.Aux[P, WithValues, _],
  mapper: Mapper.Aux[GetLabelValue.type, WithValues, Values]
): Values = {
  val state = Map("a" -> 5, "b" -> "five")
  val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
  val withValues: WithValues = ic.head(resultTuple)
  withValues.map(GetLabelValue)
}

顺便说一句,我知道如果完全摆脱LabelWithValue,我就不会有这个问题——我只是想了解如何组合类型级别的foldRight和map... - Michael Pollmeier
1个回答

6

这里的问题是您尝试在一个 HList 上进行映射,其中 HNil 静态类型为 HNil.type。一般情况下,这是行不通的,例如在以下简化情况下:

import shapeless._, ops.hlist.Mapper

val mapped1 = Mapper[poly.identity.type, HNil]
val mapped2 = Mapper[poly.identity.type, HNil.type]

mapped1会编译通过,但mapped2不会。

关键在于要将RightFolder类型中的HNil.type更改为HNil,然后使用HNil: HNil调用foldRight。这样一切都可以正常工作。

还有一些其他建议(在P的位置上解构元组代替使用IsComposite,跳过mapper上的并返回mapper.Out而不是具有类型参数,等等),但它们可能超出了本问题的范围。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Michael Pollmeier
关于您的其他建议:我之前尝试过这两种方法,但都没有成功。我创建了一个后续问题来解构元组: https://dev59.com/YZDea4cB1Zd3GeqPbGiH - Michael Pollmeier
顺便提一下,跳过 AuxValue 类型参数也不起作用,但我猜这可能与我的 RightFolder 有关,因为在更简单的情况下(例如使用 Tupler)它可以正常工作。 - Michael Pollmeier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接