Почему вывод типа не работает здесь?
Эта проблема возникла в модуле, который я пишу, но я сделал минимальный случай, который демонстрирует такое же поведение.
class Minimal[T](x : T) {
def doSomething = x
}
object Sugar {
type S[T] = { def doSomething : T }
def apply[T, X <: S[T]] (x: X) = x.doSomething
}
object Error {
val a = new Minimal(4)
Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply
Sugar[Int, Minimal[Int]](a) // works as expected
}
Проблема в том, что компилятору удается выяснить внутренний параметр для Minimal
(Int
), но затем устанавливает другое вхождение T
в Nothing
что явно не совпадает apply
, Это определенно то же самое T
Так как удаление первого параметра заставляет второй жаловаться, что T не определено.
Есть ли некоторая двусмысленность, которая означает, что компилятор не может вывести первый параметр, или это ошибка? Могу ли я обойти это изящно?
Дополнительная информация: этот код является простым примером попытки синтаксического сахара. Оригинальный код пытается сделать |(a)|
означает модуль a
где а - вектор. очевидно |(a)|
лучше, чем писать |[Float,Vector3[Float]](a)|
, но, к сожалению, я не могу использовать unary_|
чтобы сделать это проще.
Фактическая ошибка:
выводимые аргументы типа [Nothing,Minimal[Int]] не соответствуют границам параметров типа применения метода [T,X <: Sugar.S [T]]
2 ответа
Это не ошибка компилятора Scala, но, безусловно, это ограничение вывода типов в Scala. Компилятор хочет определить границу X
, S[T]
, прежде чем решить для X
, но в графе упоминается до сих пор переменная типа без ограничений T
который поэтому исправляет в Nothing
и исходит оттуда. Не возвращается T
один раз X
был полностью решен... в настоящее время вывод типа всегда происходит слева направо в этом случае.
Если ваш пример точно отражает вашу реальную ситуацию, то есть простое решение,
def apply[T](x : S[T]) = x.doSomething
Вот T
будет выведено так, что Minimal
соответствует S[T]
напрямую, а не через посредническую переменную ограниченного типа.
Обновить
Решение Джошуа также позволяет избежать проблемы определения типа T
, но совершенно по-другому.
def apply[T, X <% S[T]](x : X) = x.doSomething
Desugars к,
def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething
Переменные типа T
а также X
Теперь можно решить самостоятельно (потому что T
больше не упоминается в X
связан). Это означает, что X
выводится как Minimal
немедленно и T
решается как часть неявного поиска значения типа X => S[T]
удовлетворить неявный аргумент conv
, conforms
в scala.Predef
manufactures values of this form, and in context will guarantee that given an argument of type Minimal
, T
will be inferred as Int. You could view this as an instance of functional dependencies at work in Scala.
Есть некоторые странности с границами для структурных типов, попробуйте вместо этого использовать представление, связанное с S[T].
def apply[T, X <% S[T]] (x: X) = x.doSomething
работает отлично.