Почему здесь не работает вывод типов?

Эта проблема возникла в модуле, который я пишу, но я сделал минимальный случай, который демонстрирует такое же поведение.

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 - вектор. Ясно, что |(a)| лучше, чем писать |[Float,Vector3[Float]](a)|, но, к сожалению, я не могу использовать unary_|, чтобы упростить эту задачу.

Фактическая ошибка:

аргументы предполагаемого типа [Nothing, Minimal [Int]] не соответствуют границам параметра типа метода apply [T, X ‹: Sugar.S [T]]


person Dylan    schedule 27.04.2012    source источник


Ответы (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

десахаров,

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 производит значения этой формы и в контексте гарантирует, что для данного аргумента типа Minimal, T будет выводиться как Int. Вы можете рассматривать это как пример функциональных зависимостей, работающих в Scala.

person Miles Sabin    schedule 27.04.2012
comment
Да, в моем случае это решение работает нормально. Он также чище, чем решение Джошуа (извините, Джошуа!). Не могли бы вы тогда объяснить, почему решение Джошуа работает? - person Dylan; 27.04.2012
comment
Ответ обновлен, чтобы объяснить, почему решение Джошуа также работает. - person Miles Sabin; 28.04.2012

Есть некоторая странность с ограничениями структурных типов, попробуйте вместо этого использовать представление, привязанное к S [T].

def apply[T, X <% S[T]] (x: X) = x.doSomething отлично работает.

person Josh Gao    schedule 27.04.2012
comment
Отлично, работает, но ведь разницы между подходами быть не должно? Получение представления в этом случае - это просто приведение к суперклассу, не так ли? Означает ли это, что это исправление - всего лишь обходной путь для ошибки в компиляторе? - person Dylan; 27.04.2012
comment
Ах, теперь я понимаю, S не суперкласс - это просто представление (в некотором смысле). Таким образом, граница представления более уместна, хотя в большинстве случаев она и не требуется. - person Dylan; 27.04.2012