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

Preserving type after modification of elements of a shapeless HMap

我正在尝试使用 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)
    }
}

利用@devkat提供的参考资料,我设法细化了代码,解决了问题中提到的两个问题。在我的实际应用程序中,class 结构更加嵌套,因此我不得不使用 F 有界多态性和自类型来实现所需的结果。为了完整起见,这是我的代码的最终版本。

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))
    }
}