Почему при выводе неявных преобразований вывод типа выбирает только наиболее конкретный тип целевой ссылки? - PullRequest
4 голосов
/ 10 февраля 2011

Рассмотрим следующий простой код для создания безопасных типов.В этом первом разделе я могу создать класс типов Identity для любого типа.

scala> trait Equals[A] { def equal(a1 : A, a2 : A) : Boolean }
defined trait Equals

scala>  sealed trait Identity[A] {
     | def value : A
     | def ===(b : A)(implicit e : Equals[A]) = e.equal(value, b)
     | }
defined trait Identity

scala> implicit def ToIdentity[A](a : A) = new Identity[A] { val value = a }
ToIdentity: [A](a: A)java.lang.Object with Identity[A]

Итак, если я создаю класс типов для Equals[Int], я теперь смогу использовать мой тип безопасности, равный:

scala> implicit val EqualsInt = new Equals[Int] { def equal(i1 : Int, i2 : Int) = i1 == i2 }
EqualsInt: java.lang.Object with Equals[Int] = $anon$1@7e199049

scala> 1 === 2
res1: Boolean = false

scala> 1 === 1
res2: Boolean = true

scala> 1 === 1D
<console>:10: error: type mismatch;
 found   : Double(1.0)
 required: Int
       1 === 1D
             ^

Хорошо, пока все хорошо.Что произойдет, если я сейчас создам Equals[Any]?

scala> implicit val EqualsAny = new Equals[Any] { def equal(a1 : Any, a2 : Any) = a1 == a2 }
EqualsAny: java.lang.Object with Equals[Any] = $anon$1@141d19

scala> 1 === 1D
<console>:11: error: type mismatch;
 found   : Double(1.0)
 required: Int
       1 === 1D
             ^

Но тогда, если я скажу компилятору, что мой тип - Any, а не Int ...

scala> (1 : Any) === 1D
res6: Boolean = true

Итак, мой вопрос «почему компилятор не учитывает все типов, которые логически имеет 1?»

То есть, мое понимание состояло в том, чтоссылка типа Int логически имеет типы Int, AnyVal и Any.Во всяком случае, я исследовал немного больше, предполагая, что проблема была связана с ковариацией.Я изменил свое определение Identity:

scala> sealed trait Identity[+A] {
     | def value : A
     | def ===[B >: A : Equals](b : B) = implicitly[Equals[B]].equal(value, b)
     | }
defined trait Identity

На этот раз я получил ошибку:

scala> 1 === 1D
<console>:10: error: could not find implicit value for evidence parameter of type Equals[AnyVal]
       1 === 1D
         ^

Так что, если я создаю Equals[AnyVal], то это также работает:

scala> implicit val EqualsAnyVal = new Equals[AnyVal] { def equal(a1 : AnyVal, a2 : AnyVal) = a1 == a2 }
EqualsAnyVal: java.lang.Object with Equals[AnyVal] = $anon$1@67ce08c7

scala> 1 === 1D
res4: Boolean = true

Итак, здесь я предполагаю, что проблема заключается в отсутствии контравариантности Equals.Поэтому я пытаюсь снова (но без создания Equals[AnyVal]):

scala> trait Equals[-A] { def equal(a1 : A, a2 : A) : Boolean }
defined trait Equals

scala> 1 === 1D
res3: Boolean = true

Итак, я могу что-то увидеть, что здесь делает типер.Но мой вопрос выглядит так : почему типер не задает вопрос (для моего первого примера):

  1. 1 - это Int;с учетом возможных последствий я могу создать Identity[Int], а затем использовать метод ===.Но это не работает, потому что аргумент не является Int.Попробуйте еще раз рассмотреть альтернативные типы для 1.
  2. 1 - это AnyVal;с учетом возможных последствий я могу создать Identity[AnyVal], а затем использовать ===.Но это не работает, потому что, хотя аргумент является AnyVal, не существует неявного Equals[AnyVal] в области видимости.Попробуйте еще раз рассмотреть альтернативные типы для 1.
  3. 1 - это Any;с учетом возможных последствий я могу создать Identity[Any], а затем использовать ===.Это работает, потому что оба аргумента - Any, а область действия - Equals[Any].

Почему вывод типа учитывает только самый строгий тип 1 (т. Е. Int)?

1 Ответ

6 голосов
/ 10 февраля 2011

Здесь вы видите неявное преобразование по приоритетам, добавленное в Scala 2.8.

Согласно Спецификация языка (pdf) , Раздел 7.2 :

Если существует несколько приемлемых аргументов, которые соответствуют типу неявного параметра, наиболее конкретный из них будет выбран с использованием правил разрешения статической перегрузки (§6.26.3).

Это также механизм, который лежит в основе поведения CanBuildFrom в 2.8 коллекциях .

...