Вывод типа Scala как для универсального типа, так и для его параметра типа - почему это не работает?

Если бы я назвал самую неприятную вещь в scala, то это было бы для следующего кода:

trait G[+T]
class H[+T] extends G[T]

def f[A<:G[X], X<:Int](g :A)

val g :H[Int]
f(g)

компилятор определяет типы последнего обращения к f [H [Int], Nothing] и жалуется передо мной на свою глупость.

Однако, зная scala, он знает лучше меня. В чем причина этого? Поскольку и G, и H ковариантны относительно T, S <: G[X] with H[_] <=> S<: H[X] для любого типа S. Этот единственный недостаток заставил меня спроектировать все вокруг, избегая необходимости явно указывать типы - здесь это может выглядеть как ничто, но когда имена становятся `` реальной '' длины и практически любой метод является универсальным, и часто, работая с двумя универсальными типами, оказывается, что большая часть кода является объявлениями типов.

РЕДАКТИРОВАТЬ: вышеупомянутый случай был решен ниже Ноа, но что, когда производный класс не того же типа, что и базовый класс, как показано ниже?

trait G[+X]
class H[+X, Y] extends G[X]
class F extends G[Int]
def f[A<:G[X], X<:Int](g :A) = g

val h: H[Int, String] = ???
val g :F = ???
f(g)
f(h)

person Turin    schedule 24.06.2015    source источник


Ответы (1)


Если вы заставите A принимать параметр типа A[_], я думаю, вы можете заставить компилятор Scala согласиться с вами вместо того, чтобы просто делать все Nothing:

def f[A[_] <: G[_], X <: Int](g: A[X]) 

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

ОБНОВЛЕНИЕ

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

  trait G[+X]

  class H[+X, Y] extends G[X]

  class F extends G[Int]

  class I extends G[String]

  def f[A[_] <: G[_], X <: Int](g: A[X]) = g

  val h: H[Int, String] = new H[Int, String]
  val g: F = new F
  val i:I = new I
  f(g) //works
  f(h) //works
  f(i) // should fail and does fail
person Noah    schedule 25.06.2015
comment
Спасибо, это, безусловно, полезно и похоже, что здесь проблема решена. Однако это не поможет, если H другого типа - принимает больше параметров типа или не принимает ни одного, создавая экземпляр параметра G. Или если H вводит границы для своего параметра. Так что мне все еще интересно, есть ли причина, по которой вышеперечисленное не работает. - person Turin; 25.06.2015
comment
Вам придется привести еще один пример того, что не работает. Я почти уверен, что вы обычно можете заставить компилятор согласиться с вами. В приведенном выше примере у компилятора возникли проблемы с извлечением типа из g:A. Он может определить, что A равно H[Int], но вы не укажете X, поэтому он будет отключен, даже если вы скажете A <: G[X]. В приведенном мною примере указан тип X, чтобы компилятор мог его вывести. - person Noah; 25.06.2015
comment
@Turin метод, который я предоставил, работает для новых случаев, которые вы добавили - person Noah; 25.06.2015
comment
Вау, спасибо! Думаю, в какой-то момент меня ждет большой рефакторинг, посмотрю, решит ли он все мои практические вопросы. - person Turin; 29.06.2015