Scala вывод типа для экзистенциального типа
Рассмотрим следующий фрагмент кода, который является уменьшенной версией моей исходной проблемы:
case class RandomVariable[A](values: List[A])
case class Assignment[A](variable: RandomVariable[A], value: A)
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[Assignment[_]]): Double =
vars match {
case variable :: tail =>
val enumerated = for {value <- variable.values
extendedEvidence = evidence :+ Assignment(variable, value)
} yield enumerateAll(tail, extendedEvidence)
enumerated.sum
case Nil => 1.0
}
Это не выполняется с ошибкой времени компиляции, в которой variable
было установлено, что имеет тип RandomVariable[_0]
, когда Assignment
требуется тип Any
. Почему value
также не предполагается, что у вас есть тип _0
? Я попытался присвоить имя экзистенциального типа, чтобы дать подсказку для компилятора с помощью case (variable: RandomVariable[T forSome {type T}]) :: tail =>
, но это также не будет компилироваться (говоря, что он не мог найти тип T, о котором мне также было бы интересно объяснение).
Для дальнейшей мотивации рассмотрим, когда мы фиксируем параметр типа следующим образом:
case variable :: tail =>
def sum[A](variable: RandomVariable[A]): Double = {
val enumerated = for {value <- variable.values
extendedEvidence = evidence :+ Assignment(variable, value)
} yield enumerateAll(tail, extendedEvidence)
enumerated.sum
}
sum(variable)
Это компилируется без предупреждений/ошибок. Есть ли что-то, что я могу изменить в первом примере, чтобы не требовать этой дополнительной функции?
EDIT. Чтобы быть более явным, я хочу знать, почему value
не выводится типа _0
, хотя variable
имеет тип _0
, и каждое значение приходит от a List[_0]
в variable
. Кроме того, я хотел бы знать, есть ли какие-либо дополнительные способы сообщить компилятору об этом факте (кроме того, что он записывает тип в функции, как я уже говорил выше).
Ответы
Ответ 1
Другое компилируемое решение, более чистое (?), чем использование функции для захвата типа. Тем не менее, это еще более озадачивает вопрос о том, почему вывод типа не выполняется в исходном случае.
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[SingleAssignment[_]]): Double = vars match {
case ([email protected](values)) :: tail =>
val enumeration = for {value <- values
assignment = SingleAssignment(variable, value)
extendedEvidence = evidence :+ assignment
} yield enumerateAll(tail, extendedEvidence)
enumeration.sum
case Nil => 1.0
}
Он также возвращает следующее предупреждение:
scala: match may not be exhaustive.
It would fail on the following input: List((x: questions.RandomVariable[?] forSome x not in questions.RandomVariable[?]))
def enumerateAll(vars: List[RandomVariable[_]], evidence: List[SingleAssignment[_]]): Double = vars match {
Который я не могу расшифровать с этой публикации. Кроме того, запуск его с помощью нескольких тестовых примеров дает желаемый результат без ошибки совпадения с использованием RandomVariable
для int, double и string в списке параметров.
Ответ 2
Нельзя ли связать типы RandomVariable и Assignment вместе?
def [A] enumerateAll(vars: List[RandomVariable[A]], evidence: List[Assignment[A]]): Double =
на самом деле, вы можете быть более разрешительным и просто сказать
def [A] enumerateAll(vars: List[RandomVariable[A]], evidence: List[Assignment[_ <: A]]): Double =
Ответ 3
Код ошибки дает некоторое представление о решении.
<console>:15: error: type mismatch;
found : RandomVariable[_0] where type _0
required: RandomVariable[Any]
Note: _0 <: Any, but class RandomVariable is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
extendedEvidence = evidence :+ Assignment(variable, value)
Это говорит вам, что он видел более специфический тип, чем он предполагал, и даже предложил сделать RandomVariable допустимым ковариант A. Это позволило бы ему изменять тип вниз, когда это необходимо.
case class RandomVariable[+A](values: List[A])
В качестве альтернативы вы можете явно задать общий тип в enumerateAll для обоих параметров. Таким образом, он может вывести соответствующий тип вместо принудительного вывода Any. Это определение не требует случайного переменного ковариантного изменения, так как оба параметра одного типа.
def enumerateAll[A](vars: List[RandomVariable[A]], evidence: List[Assignment[A]]): Double =
Этот вопрос может помочь в объяснении. Почему пример не компилируется, а как работает (co-, contra- и in) дисперсия?