这可能是一系列问题,但是我必须从某个地方开始。我试图创建一个映射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"))
答案 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