Scala: значение метода не соответствует границам типа

#scala #types

#scala #типы

Вопрос:

Я пытаюсь написать код, который сохраняет типизированное значение переменной. Вот пример кода:

 class MethodTypeTest
{
    trait TSM[T <: TSM[T]]
    {
        def fit(): FiM[T]
    }

    trait FiM[T <: TSM[_]]
    {
        val fitErrors = mutable.HashMap[String, Double]()
    }

    class TS
    {
        //*** Here I am saving the value of the variable after some computation.
        var bestModel: Option[FiM[_]] = None
    }

    // Now define for SMA
    class SMAM extends FiM[SMA]
    {
        fitErrors("RMSE") = 10
    }
    class SMA extends TSM[SMA]
    {
        override def fit() = new SMAM()
    }
    // Now define for DMA
    class DMAM extends FiM[DMA]
    {
        fitErrors("RMSE") = 10.5
    }
    class DMA extends TSM[DMA]
    {
        override def fit() = new DMAM()
    }

    def main(args: Array[String]): Unit =
    {
        val ts = new TS
        val sma = new SMA
        val dma = new DMA
        val fms: Array[FiM[_]] = Array[FiM[_]](sma.fit(), dma.fit())
        val bestModel: FiM[_] = fms.minBy(m => m.fitErrors("RMSE"))
        // ******** Error in below line ******
        // Error:(48, 24) type arguments [_$3] do not conform to trait FiM's type parameter bounds [T <: MethodTypeTest.this.TSM[_]]
        //        ts.bestModel = Some(bestModel)
        ts.bestModel = Some(bestModel)
    }
}
 

Похоже, компилятор жалуется, что граница типа слишком открыта в последней строке. Я намеренно оставил открытым тип bestModel в инструкции var bestModel: Option[FiM[_]] = None при определении class TS , поскольку это значение будет вычислено позже для одного из FIM. Как / где я могу указать привязку к типу, чтобы это работало?

Комментарии:

1. Можете ли вы сделать TS неизменяемым и выделить его в результате вычисления fms.minBy ? т.е. case class TS(bestModel: FiM[_]) и тогда val bestModel = TS(bestModel) ?

2. Фактический класс TS имеет множество других переменных, которые инициализируются, только одна переменная, которую необходимо установить позже. Вот почему я не мог использовать класс case.

Ответ №1:

Я смог скомпилировать это (с помощью scastie с использованием Scala 2.13.4), добавив в границы типов _ <: TSM[_] везде, где у вас есть FiM[_] аннотация типа:

 class TS {
    var bestModel: Option[FiM[_ <: TSM[_]]] = None
}
//...

val fms: Array[FiM[_ <: TSM[_]]] = Array(sma.fit(), dma.fit())
val bestModel: FiM[_ <: TSM[_]] = fms.minBy(m => m.fitErrors("RMSE"))
 

Я считаю, что это необходимо, потому что в противном случае он считает, что параметр неизвестного TSM[_] типа массива должен совпадать с параметром val , поэтому указание, что он тоже должен быть шаблоном, позволяет избежать этого.