带参数的无形 HList 多态映射
Shapeless HList polymorphic map with an argument
给定 Label[A](String)
的 HList,我想将其映射到 LabelWithValue[A](Label[A], A)
的 HList,其中实际值来自 Map[String, Any]
。在下面的示例中,我只是在方法中定义了值映射,想象一下这些值来自数据库。
下面的代码有效,但它非常笨拙,因为它使用了全局变量。相反,我想将 Map[String, Any]
传递给 GetLabelWithValue
。不过我没有找到办法,因为 getValues
的调用者隐式创建了一个映射器,而此时值映射还不存在。我尝试自己创建一个 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
}
}
这是 GetLabelWithValue 的替代实现,其行为方式相同:
object GetLabelWithValue extends Poly1 {
implicit def caseLabel[A] = at[Label[A]] { label ⇒
LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
}
}
我绝不是无形的大师,但我首先想到的是:
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)
}
我并不是说没有更好的方法,但这似乎很合理 - 您可以使用 fold 捕获它并根据它来决定,而不是全局状态。可能会给你比你需要的更多的力量(因为你可以在折叠之间改变地图,但是......)
当您想在方法中使用它时,这是完整的解决方案(基于 KadekM 的解决方案)。困难的一点是从元组中提取类型(这是折叠的结果)。
import shapeless._
import shapeless.ops.hlist._
import shapeless.ops.tuple.IsComposite
object Main extends App {
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
object combineLabelWithValue extends Poly2 {
implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
case (label, (acc, values)) ⇒
(LabelWithValue(label, values.get(label.name).asInstanceOf[A]) :: acc, values)
}
}
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, Out, P](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
ic: IsComposite.Aux[P, Out, _]
): Out = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
ic.head(resultTuple)
}
}
给定 Label[A](String)
的 HList,我想将其映射到 LabelWithValue[A](Label[A], A)
的 HList,其中实际值来自 Map[String, Any]
。在下面的示例中,我只是在方法中定义了值映射,想象一下这些值来自数据库。
下面的代码有效,但它非常笨拙,因为它使用了全局变量。相反,我想将 Map[String, Any]
传递给 GetLabelWithValue
。不过我没有找到办法,因为 getValues
的调用者隐式创建了一个映射器,而此时值映射还不存在。我尝试自己创建一个 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
}
}
这是 GetLabelWithValue 的替代实现,其行为方式相同:
object GetLabelWithValue extends Poly1 {
implicit def caseLabel[A] = at[Label[A]] { label ⇒
LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
}
}
我绝不是无形的大师,但我首先想到的是:
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)
}
我并不是说没有更好的方法,但这似乎很合理 - 您可以使用 fold 捕获它并根据它来决定,而不是全局状态。可能会给你比你需要的更多的力量(因为你可以在折叠之间改变地图,但是......)
当您想在方法中使用它时,这是完整的解决方案(基于 KadekM 的解决方案)。困难的一点是从元组中提取类型(这是折叠的结果)。
import shapeless._
import shapeless.ops.hlist._
import shapeless.ops.tuple.IsComposite
object Main extends App {
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
object combineLabelWithValue extends Poly2 {
implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
case (label, (acc, values)) ⇒
(LabelWithValue(label, values.get(label.name).asInstanceOf[A]) :: acc, values)
}
}
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, Out, P](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
ic: IsComposite.Aux[P, Out, _]
): Out = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
ic.head(resultTuple)
}
}