修改无形HMap的元素后保留类型

时间:2016-11-02 13:48:49

标签: scala shapeless

我尝试使用HList来模拟共享同一父级的对象集合。我希望能够:

  • 使用索引访问集合的元素
  • 使用父
  • 中定义的方法转换集合的某些元素

我能够解决上面的第一点(在下面的方法withIndex中)。但是,IntelliJ IDEA显示消息Expression of type at.Out doesn't conform to expected type T的突出显示错误。是否有一个隐含的,我可以添加,以摆脱这个?

对于第二点(下面的方法modify),我收到编译错误

Error:(31, 13) type mismatch;
 found   : result.type (with underlying type Value)
 required: T
        result

为了摆脱asInstanceOf投射,我是否可以添加隐含的内容,以便我可以应用modify方法?此外,即使应用modify方法,我也希望能够获得相同类型的对象。如您所见,AdditiveMultiplicative正确实施了modify方法。我正在努力寻找一种方法来提供证据确实是这种情况......

以上引用的代码:

import shapeless._
import shapeless.ops.hlist.At

sealed trait Value {

    val value: Double

    def modify(newValue: Double): Value
}

case class Additive(value: Double) extends Value {

    def modify(newValue: Double): Additive = this.copy(value = value + newValue)
}

case class Multiplicative(value: Double) extends Value {

    def modify(newValue: Double): Multiplicative = this.copy(value = value * newValue)
}

case class Collection[L <: HList](values: L)
                             (implicit
                              val ev: LUBConstraint[L, Value]) {

    def withIndex[T <: Value](index: Nat)(implicit at: At.Aux[L, index.N, T]): T = values(index)

    def modify[T <: Value](index: Nat, newValue: Double)(implicit at: At.Aux[L, index.N, T]): T = {
        val value = values(index)
        val result = value.asInstanceOf[T].modify(newValue)

        result
    }
}

object App {

    def main(args: Array[String]): Unit = {

        val val1 = Additive(1.0)
        val val2 = Additive(2.0)

        val val3 = Multiplicative(3.0)

        val coll = Collection(val1 :: val2 :: val3 :: HNil)

        val copyVal1: Additive = coll.withIndex(0)
        val copyVal2: Additive = coll.withIndex(1)
        val copyVal3: Multiplicative = coll.withIndex(2)

        coll.modify(0, 1.0)
        coll.modify(1, 2.0)
        coll.modify(2, 3.0)
    }
}

1 个答案:

答案 0 :(得分:0)

使用@devkat提供的参考,我设法优化代码以解决问题中陈述的两个问题。在我的实际应用程序中,类结构更嵌套,所以我不得不使用F-bounded多态和自我类型来实现所需的结果。为了完整起见,这是我的代码的最终版本。

import shapeless._
import shapeless.ops.hlist.At

sealed trait Value[+V <: Value[V]] {
    this: V =>

    val value: Double

    def modify(newValue: Double): V

    def chainModify(newValues: List[Double]): V = {
        newValues.foldLeft(this)((obj, v) => obj.modify(v))
    }
}

case class Additive(value: Double) extends Value[Additive] {

    def modify(newValue: Double) = this.copy(value = value + newValue)
}

case class Multiplicative(value: Double) extends Value[Multiplicative] {

    def modify(newValue: Double) = this.copy(value = value * newValue)
}

trait NonCommutative extends Value[NonCommutative] {

}

case class Divisive(value: Double) extends NonCommutative with Value[Divisive] {

    def modify(newValue: Double) = this.copy(value = value / newValue)
}

case class Collection[L <: HList](values: L)
                                 (implicit val ev: LUBConstraint[L, Value[_]]) {

    def withIndex[T <: Value[T]](index: Nat)(implicit at: At.Aux[L, index.N, T]): T = values(index)

    def modify[T <: Value[T]](index: Nat, newValue: Double)(implicit at: At.Aux[L, index.N, T]): T = {
        val value = values(index)
        val result = value.modify(newValue)

        result
    }
}

object App {

    def main(args: Array[String]): Unit = {

        val val1 = Additive(1.0)
        val val2 = Additive(2.0)

        val val3 = Multiplicative(3.0)

        val val4 = Divisive(4.0)

        val coll = Collection(val1 :: val2 :: val3 :: val4 :: HNil)

        val copyVal1: Additive = coll.withIndex(0)
        val copyVal2: Additive = coll.withIndex(1)
        val copyVal3: Multiplicative = coll.withIndex(2)
        val copyVal4: Divisive = coll.withIndex(3)

        println(copyVal3.chainModify(1.0 :: 2.0 :: 3.0 :: Nil))

        println(coll.modify(0, 1.0))
        println(coll.modify(1, 2.0))
        println(coll.modify(2, 3.0))
        println(coll.modify(3, 4.0))
    }
}