使用HList的映射无法编译

时间:2019-07-18 16:35:43

标签: scala shapeless

这可能是一系列问题,但是我必须从某个地方开始。我试图创建一个映射String => HList,并在运行时通过键访问它们,但是编译器对此不满意。看下面的示例(您可以复制并粘贴并运行它):

映射的原因是在运行时访问这些Exe。我正在添加一个功能,例如runExe可以更好地显示运行时用例:

import shapeless._

import scala.util.Random
trait ExeLike[Args <: HList, +O] {
  def id: String
  def map(ar:Args):O
  def numArgs:Int

}

case class WithRunTime[Args <:HList, O](args:Args, exeLike: ExeLike[Args, O]) {
  def run: O = exeLike.map(args)
}

case class Exe0[O](id:String, map0: () => O) extends ExeLike[HNil, O] {
  override def numArgs: Int = 0
  override def map(args: HNil): O = map0()
}

case class Exe1[I, O](id:String, map1: I => O) extends ExeLike[I :: HNil, O] {
  override def numArgs: Int = 1
  override def map(args: I :: HNil): O = map1(args.head)
}

case class Exe2[I1, I2, O](id:String, map2: (I1, I2) => O) extends ExeLike[I1 :: I2 :: HNil, O] {
  override def numArgs: Int = 2
  override def map(args: I1 :: I2 :: HNil): O = map2(args.head, args(1))
}


object PlayGroundShapeless extends App {
//  def runExe[Args <: HList, +O](name:String, allFuncs:Map[String, ExeLike[Args, O]) = WithRunTime(10 :: 12 :: HNil, allFuncs(name))

  val ToString = Exe1("toString", (int: Int) => int.toString)
  val Sum = Exe2("Sum", (a0: Int, a1: Int) => a0 + a1)
  val RandomInt = Exe0("randomNumber", () => Random.nextInt())
  val allFuncs = List(ToString, Sum, RandomInt).map(a => a.id -> a).toMap


  val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs("Sum")) //dose not compile
  //val runnableSum = WithRunTime(10 :: 12 :: HNil, Sum) //compile

  println(runnableSum.run)

}

编译器错误:

Error:(448, 21) no type parameters for method apply: (args: Args, exeLike: ExeLike[Args,O])WithRunTime[Args,O] in object WithRunTime exist so that it can be applied to arguments (Int :: Int :: shapeless.HNil, Product with Serializable with ExeLike[_ >: shapeless.HNil with Int :: Int :: shapeless.HNil with Int :: shapeless.HNil <: shapeless.HList, Any])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Product with Serializable with ExeLike[_ >: shapeless.HNil with Int :: Int :: shapeless.HNil with Int :: shapeless.HNil <: shapeless.HList, Any]
 required: ExeLike[?Args,?O]
  val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs("Sum"))
Error:(448, 36) type mismatch;
 found   : Int :: Int :: shapeless.HNil
 required: Args
  val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs("Sum"))
Error:(448, 59) type mismatch;
 found   : Product with Serializable with ExeLike[_ >: shapeless.HNil with Int :: Int :: shapeless.HNil with Int :: shapeless.HNil <: shapeless.HList, Any]
 required: ExeLike[Args,O]
  val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs("Sum"))

1 个答案:

答案 0 :(得分:0)

问题是allFuncs具有存在类型

Map[String, ExeLike[_ >: (HNil with (Int :: Int :: HNil) with (Int :: HNil)) <: HList, Any]]`

所以allFuncs("Sum")的类型是

ExeLike[_ >: (HNil with (Int :: HNil) with (Int :: Int :: HNil)) <: HList, Any]

所以我们应该垂头丧气

val runnableSum = WithRunTime((10 :: 12 :: HNil).asInstanceOf[HNil with (Int :: HNil) with (Int :: Int :: HNil)], allFuncs("Sum"))

println(runnableSum.run) // 22

如果您想使其更安全,因为allFuncs应该根据参数值返回不同的类型Exe0[Int]Exe1[Int, String]Exe2[Int, Int, Int],因此您可以应该将allFuncs设为Poly

object allFuncs extends Poly1 {
  implicit val toStr: Case.Aux["toString", Exe1[Int, String]] = at(_ => ToString)
  implicit val sum: Case.Aux["Sum", Exe2[Int, Int, Int]] = at(_ => Sum)
  implicit val rnd: Case.Aux["randomNumber", Exe0[Int]] = at(_ => RandomInt)
}

val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs["Sum"]("Sum"))

println(runnableSum.run) // 22

在2.12版

object allFuncs extends Poly1 {
  implicit val toStr: Case.Aux[Witness.`"toString"`.T, Exe1[Int, String]] = at(_ => ToString)
  implicit val sum: Case.Aux[Witness.`"Sum"`.T, Exe2[Int, Int, Int]] = at(_ => Sum)
  implicit val rnd: Case.Aux[Witness.`"randomNumber"`.T, Exe0[Int]] = at(_ => RandomInt)
}

import shapeless.syntax.singleton._
val runnableSum = WithRunTime(10 :: 12 :: HNil, allFuncs[Witness.`"Sum"`.T]("Sum".narrow)) 
println(runnableSum.run) // 22