Что такое понятие "слабое соответствие" в Scala?

Я только недавно столкнулся с термином "Слабое соответствие" (в ответе пользователя "Переполнение стека" на вопрос " Как настроить неявное преобразование, чтобы разрешить арифметику между числовыми типами?").

Что это?

3 ответа

Решение

3.5.3 Слабое соответствие В некоторых ситуациях Scala использует более общее отношение соответствия. Тип S слабо соответствует типу T, написанному S <: w T, если S <: T или оба S и T являются типами примитивных чисел и S предшествует T в следующем порядке.

  • Байт <: w короткий
  • Байт <: w символ
  • Короткая <: w Int
  • Int <: w Long
  • Long <: w Float
  • Поплавок <: w Double

Слабая наименьшая верхняя граница является наименьшей верхней границей в отношении слабого соответствия.

Где это используется? С одной стороны, это определяет тип if выражения:

Тип условного выражения является слабой наименьшей верхней границей (§3.5.3) типов e2 и e3

В Scala 2.7.x это будет по типу AnyValнаименее верхняя граница Int а также Double, В 2.8.x он печатает как Double,

scala> if (true) 1 else 1d
res0: Double = 1.0

Так же:

scala> try { 1 } catch { case _ => 1.0 }
res2: Double = 1.0

scala> (new {}: Any) match { case 1 => 1; case _ => 1.0 }
res6: Double = 1.0

scala> def pf[R](pf: PartialFunction[Any, R]): PartialFunction[Any, R] = pf
pf: [R](pf: PartialFunction[Any,R])PartialFunction[Any,R]

scala> pf { case 1 => 1; case _ => 1d }
res4: PartialFunction[Any,Double] = <function1>

Другое место, где он используется, находится в выводе типа:

scala> def foo[A](a1: A, a2: A): A = a1
foo: [A](a1: A,a2: A)A

scala> foo(1, 1d)
res8: Double = 1.0

scala> def foos[A](as: A*): A = as.head
foos: [A](as: A*)A

scala> foos(1, 1d)
res9: Double = 1.0

А также для простого числового расширения:

Численное расширение. Если e имеет примитивный тип числа, который слабо соответствует (§3.5.3) ожидаемому типу, он расширяется до ожидаемого типа, используя один из методов преобразования чисел 6.26 Неявные преобразования 97 toShort, toChar, toInt, toLong, toFloat, toDouble определено в §12.2.1. Ожидаемый тип - это примитивный числовой тип Byte, Short или Char, а выражение e - это целочисленный литерал, подходящий в диапазоне этого типа, он преобразуется в тот же литерал в этом типе.

scala> 1: Double
res10: Double = 1.0

ОБНОВИТЬ

Как указал Даниэль, спецификация неверна в отношении того, какие типы имеют слабое соответствие. Давайте спросим сам компилятор:

scala> :power
** Power User mode enabled - BEEP BOOP      **
** scala.tools.nsc._ has been imported      **
** New vals! Try repl, global, power        **
** New cmds! :help to discover them         **
** New defs! Type power.<tab> to reveal     **

scala> settings.maxPrintString = 10000


scala> import global.definitions._
import global.definitions._

scala> (for{c1 <- ScalaValueClasses;
      c2 <- ScalaValueClasses
      isNSC = isNumericSubClass(c1, c2)
      if isNSC
  } yield ("isNumericSubClass (%s, %s) = %b" format (c1, c2, isNSC))).mkString("\n")


res5: String =
isNumericSubClass (class Byte, class Byte) = true
isNumericSubClass (class Byte, class Short) = true
isNumericSubClass (class Byte, class Int) = true
isNumericSubClass (class Byte, class Long) = true
isNumericSubClass (class Byte, class Float) = true
isNumericSubClass (class Byte, class Double) = true
isNumericSubClass (class Short, class Short) = true
isNumericSubClass (class Short, class Int) = true
isNumericSubClass (class Short, class Long) = true
isNumericSubClass (class Short, class Float) = true
isNumericSubClass (class Short, class Double) = true
isNumericSubClass (class Int, class Int) = true
isNumericSubClass (class Int, class Long) = true
isNumericSubClass (class Int, class Float) = true
isNumericSubClass (class Int, class Double) = true
isNumericSubClass (class Long, class Long) = true
isNumericSubClass (class Long, class Float) = true
isNumericSubClass (class Long, class Double) = true
isNumericSubClass (class Char, class Int) = true
isNumericSubClass (class Char, class Long) = true
isNumericSubClass (class Char, class Char) = true
isNumericSubClass (class Char, class Float) = true
isNumericSubClass (class Char, class Double) = true
isNumericSubClass (class Float, class Float) = true
isNumericSubClass (class Float, class Double) = true
isNumericSubClass (class Double, class Double) = true

Чтобы завершить ответ Сандора, эта новая функция в 2.8 все еще запекается (и исправлена).

В этой теме Эссер раскрывает неприятный побочный эффект:

scala> val a= 10 
a: Int = 10 

scala> val b= 3 
b: Int = 3 

scala> if (b!=0) a/b else Double.NaN 
res0: Double = 3.0 

scala> def div1(a: Int, b: Int) = if (b!=0) a/b else Double.NaN 
div1: (a: Int,b: Int)Double 

scala> def div2(a: Int, b: Int): Double = if (b!=0) a/b else Double.NaN 
div2: (a: Int,b: Int)Double 

scala> div1(10,3) 
res1: Double = 3.0 

scala> div2(10,3) 
res2: Double = 3.3333333333333335 

Кажется интересным, потому что неявно найденный тип результата Double и результат 3,0.
Если Double задан явно, результат равен 3.33...

В этой теме Мартин Одерский добавляет (21 июня):

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

Это благоприятствовало Float => Float метод сложения на Int над Int => Int метод, если тип результата был Float.
Я пытался быть консервативным в своем переходе к слабому соответствию, так как требовал слабого соответствия только там, где это выглядело абсолютно необходимым.
Но теперь кажется, что консервативность стала причиной проблемы, на которую мы смотрим!

И еще один релиз Scala RC;)


Подтверждено в этой теме Мартином Одерским (22 июня):

Таким образом, будет RC7 с тремя изменениями от RC6:

  1. val x: Double = 10/3 дам 3.0не 3.3333333 - это была регрессия, которую я упоминал
  2. [...]
  3. [...]

Вот и все. Теперь наши приоритеты - как можно быстрее развернуть 2.8 и в то же время избежать действительно плохих регрессий, таких как (1) выше.

График:

  • Мы будем ждать еще одну неделю, чтобы получить отзывы о RC6.
  • Мы выпустим RC7 в начале следующей недели.
    Если больше никаких проблем не появится, RC7 превратится в 2,8 финала через 10-14 дней после выпуска.

(так что около 12 июля, я полагаю, но это предположение принадлежит только мне;))

Согласно спецификации Scala lang 2.8:
http://www.scala-lang.org/archives/downloads/distrib/files/nightly/pdfs/ScalaReference.pdf

3.5.3 Слабое соответствие
В некоторых ситуациях Scala использует более общее отношение соответствия. Тип S слабо соответствует типу T, написанному S<: w T, если S <: T или оба S и T являются типами примитивных чисел и S предшествует T в следующем порядке.
Байт <: w короткий
Байт <: w символ
Короткая <: w Int
Int <: w Long
Long <: w Float
Поплавок <: w Double
Слабая наименьшая верхняя граница является наименьшей верхней границей в отношении слабого соответствия.

Другие вопросы по тегам