带有参数的无形HList多态映射

Mic*_*ier 3 scala implicits shapeless

给定一个HList Label[A](String)我想将它映射到HList LabelWithValue[A](Label[A], A),其中实际值来自a Map[String, Any].在下面的示例中,我只是在方法中定义了值的映射,只是想象值来自数据库.

下面的工作,但它是非常veckery hacky因为它使用全局变量.相反,我想Map[String, Any]进入GetLabelWithValue.我没有找到方法,因为调用者getValues隐式创建了一个Mapper,并且此时值的映射尚不存在.我自己尝试创建一个Mapper,但我的类型级编程技能还不够好.

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

object Main extends App {
  case class Label[A](name: String)
  case class LabelWithValue[A](label: Label[A], value: A)

  // TODO: avoid the horrible global state - pass in the Map as a parameter
  var horribleGlobalState: Map[String, Any] = _
  object GetLabelWithValue extends (Label ~> LabelWithValue) {
    def apply[A](label: Label[A]) =
        LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
  }

  val label1 = Label[Int]("a")
  val label2 = Label[String]("b")
  val labels = label1 :: label2 :: HNil
  val labelsWithValues: LabelWithValue[Int] :: LabelWithValue[String] :: HNil = getValues(labels)
  println(labelsWithValues)

  def getValues[L <: HList, M <: HList](labels: L)(
    implicit mapper: Mapper.Aux[GetLabelWithValue.type, L, M]) = {

    horribleGlobalState = Map("a" -> 5, "b" -> "five")
    labels map GetLabelWithValue
  }
}
Run Code Online (Sandbox Code Playgroud)

以下是GetLabelWithValue的替代实现,其行为方式相同:

object GetLabelWithValue extends Poly1 {
  implicit def caseLabel[A] = at[Label[A]] { label ?
    LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
  }
}
Run Code Online (Sandbox Code Playgroud)

Kad*_*ekM 5

我绝不是没有形状的大师,但这是我想到的第一件事:

object Main extends App {
  case class Label[A](name: String)
  case class LabelWithValue[A](label: Label[A], value: A)

  object combine extends Poly2 {
    implicit def workS[A <: HList, B] = at[Label[B], (Map[String, Any], A)] {
      case (i, (map, res)) ?
        (map, LabelWithValue(i, map.get(i.name).asInstanceOf[B]) :: res)
    }
  }

  var state: Map[String, Any] = Map("a" -> 5, "b" -> "five")

  val label1 = Label[Int]("a")
  val label2 = Label[String]("b")

  val labels = label1 :: label2 :: HNil
  val mapped = labels.foldRight((state, HNil))(combine)._2
  println(mapped)
}
Run Code Online (Sandbox Code Playgroud)

我不是说没有更好的方法,但这似乎很合理 - 而不是全局状态,你使用折叠捕获它并根据它来决定.可能会给你比你需要的更多的力量(因为你可以在折叠之间改变地图,但......)