Типы Scala: наименьшие верхние границы

Я пытаюсь параметризовать некоторые методы с очень общими параметрами типа.

Например, в REPL я сначала определяю:

trait Term
case class FunctionalTerm[+T <: Term](t: T) extends Term

Интуитивно следующий метод принимает Term и FunctionalTerm и возвращает что-то с типом наименьшей верхней границы типа переданного термина и типа аргумента FunctionalTerm:

def ex1[T1 <: Term, T3 <: X, FunctionalTerm[T1] <: X, X <: R, R <: Term](t1: FunctionalTerm[T1], s: T3): R = sys.error("TODO")

Пока все хорошо в REPL.

Затем я определяю ex2 как вспомогательную функцию, которая выполняет ту же операцию, что и ex1, но с переставленными входными аргументами:

def ex2[T2 <: Term, T3 <: X, FunctionalTerm[T2] <: X, X <: R, R <: Term](s: T3, t2: FunctionalTerm[T2]): R = ex1(t2,s)

Попытка определить ex2 в REPL дает следующую ошибку:

error: inferred type arguments [T2,T3,FunctionalTerm,T3,T3] do not conform to method ex1's type parameter bounds [T1 <: Term,T3 <: X,FunctionalTerm[T1] <: X,X <: R,R <: Term]
         ex1(t2,s)
         ^
error: type mismatch;
 found   : FunctionalTerm[T2]
 required: FunctionalTerm[T1]
         ex1(t2,s)
             ^
error: type mismatch;
 found   : T3(in method ex2)
 required: T3(in method ex1)
         ex1(t2,s)
                ^
error: type mismatch;
 found   : R(in method ex1)
 required: R(in method ex2)
         ex1(t2,s)
            ^

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

Поскольку список аргументов типа ex2 такой же, как и у ex1, но с поменявшимися местами T1 и T2, я не понимаю, что это неправильно, или как это исправить.

Любая помощь будет очень признательна!

Обновить

Наименьшие верхние границы были отвлекающим маневром. Пример можно перегнать дальше.

Следующие две функции могут быть определены в REPL без ошибок:

def ex1[T1 <: Term, FunctionalTerm[T1] <: Term](t1: FunctionalTerm[T1]): Term = sys.error("TODO: ex1")
def ex2[T2 <: Term, FunctionalTerm[T2] <: Term](t2: FunctionalTerm[T2]): Term = ex1(t2)

Введение дополнительного параметра X, кажется, вызывает проблему. Я могу определить следующее в REPL:

def ex3[T1 <: Term, FunctionalTerm[T1] <: X, X <: Term](t1: FunctionalTerm[T1]): Term = sys.error("TODO: ex3")

Но попытка впоследствии определить:

def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)

дает ошибку:

error: inferred type arguments [T2,FunctionalTerm,Nothing] do not conform to method ex3's type parameter bounds [T1 <: Term,FunctionalTerm[T1] <: X,X <: Term]
       def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)
                                                                                               ^
error: type mismatch;
 found   : FunctionalTerm[T2]
 required: FunctionalTerm[T1]
       def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)
                                                                                                   ^

Итак, я думаю, возникает вопрос: почему параметр X, неиспользуемый в подписи, имеет такой эффект?


person Andrew Bate    schedule 16.05.2012    source источник
comment
Я забыл сказать, что использую Scala 2.10-M3.   -  person Andrew Bate    schedule 16.05.2012
comment
Однако вы не используете более высокие виды :-)   -  person oxbow_lakes    schedule 16.05.2012
comment
@oxbow_lakes О... я исправлю   -  person Andrew Bate    schedule 16.05.2012
comment
Кроме того, ошибки компиляции вашего примера не из того же кода - они указывают на ошибку в тесте, которую я нигде не вижу в вашем примере.   -  person oxbow_lakes    schedule 16.05.2012
comment
@oxbow_lakes исправлено. Извинения   -  person Andrew Bate    schedule 16.05.2012
comment
И класс Filler() посторонний. Я написал об этом Майлзу и Адриану в твиттере — мой ответ, вероятно, неверен, так как я не волшебник шрифтов!   -  person oxbow_lakes    schedule 16.05.2012


Ответы (2)


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

scala> trait Term
defined trait Term

scala> case class FunctionalTerm[+T <: Term](t: T) extends Term
defined class FunctionalTerm

scala> def ex1[T <: Term](t1 : FunctionalTerm[T], s : T) : T = s
ex1: [T <: Term](t1: FunctionalTerm[T], s: T)T

scala> class A extends Term ; class B extends A ; class C extends A
defined class A
defined class B
defined class C
scala> ex1(new FunctionalTerm(new B), new C)
res0: A = C@4ceeb514

Обратите внимание на предполагаемый тип результата (который эквивалентен R вашего исходного более сложного определения)... это A, который является LUB B и C.

Теперь перевернутая версия тривиальна и просто работает,

scala> def ex2[T <: Term](s : T, t1 : FunctionalTerm[T]) : T = s
ex2: [T <: Term](s: T, t1: FunctionalTerm[T])T

scala> ex2(new C, new FunctionalTerm(new B))
res1: A = C@522ddcec 
person Miles Sabin    schedule 16.05.2012
comment
Оказывается, заявленное поведение НЕ является ошибкой. Использование FunctionalTerm[T1] ‹: X и FunctionalTerm[T2] ‹: X в исходном примере объявило параметр типа метода с именем FunctionalTerm, который не связан с классом case с тем же именем. Однако я отмечаю это как ответ, поскольку то, что было предложено, достигло того, что мне нужно было сделать. - person Andrew Bate; 17.05.2012
comment
Ну да! ... удивительно, что ни я, ни @oxbow_lakes этого не заметили! - person Miles Sabin; 17.05.2012

Я собираюсь упростить ваш пример (я использую 2.9.1):

scala> trait Term; case class FunctionalTerm[+T <: Term](t: T) extends Term;
defined trait Term
defined class FunctionalTerm

scala> def ex1[T1 <: Term, T3 <: X, FunctionalTerm[T1] <: X, X <: R, R <: Term](t1: FunctionalTerm[T1], s: T3): R = sys.error("TODO")
ex1: [T1 <: Term, T3 <: X, FunctionalTerm[T1] <: X, X <: R, R <: Term](t1: FunctionalTerm[T1], s: T3)R

Теперь объявим второй метод:

scala> def ex2[T2 <: Term, T3 <: X, FunctionalTerm[T2] <: X, X <: R, R <: Term](s: T3, t2: FunctionalTerm[T2]): R = ex1(t2, s)
<console>:11: error: inferred type arguments [T2,T3,FunctionalTerm,T3,T3] do not conform to method ex1's type parameter bounds [T1 <: Term,T3 <: X,FunctionalTerm[T1] <: X,X <: R,R <: Term]
       def ex2[T2 <: Term, T3 <: X, FunctionalTerm[T2] <: X, X <: R, R <: Term](s: T3, t2: FunctionalTerm[T2]): R = ex1(t2, s)
                                                                                                                    ^

Подозрительные вещи (для меня) — это предполагаемые типы [T2,T3,FunctionalTerm,T3,T3], в частности FunctionalTerm. FunctionalTerm является конструктором типа * -> *но метод ожидает тип * в этой позиции.

Я бы сказал, что это (предположительно) ошибка, и, следовательно, вы должны отправить ее в список рассылки scala-user (прежде чем создавать тикет) — Адриаан Мурс и Майлз Сабин. у них гораздо больше шансов получить развернутый ответ относительно того, прав ли я.

person oxbow_lakes    schedule 16.05.2012
comment
Большое спасибо - я подозревал, что это ошибка, но мои ограниченные знания о типах заставили меня насторожиться. Я обновлю, когда что-то решу. - person Andrew Bate; 16.05.2012