Каково понятие “слабого соответствия” в Scala?

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

Что это?

27
задан Community 23 May 2017 в 10:28
поделиться

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
  • Float <: w Double

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

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

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

В Scala 2.7.x это будет по типу AnyVal , наименьшей верхней границе Int и Двойной . В версии 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
20
ответ дан 28 November 2019 в 05:43
поделиться

Чтобы завершить ответ Сандора , эта новая функция в 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 июля, но это мое предположение;))

5
ответ дан 28 November 2019 в 05:43
поделиться

Согласно Scala lang spec 2.8:
http://www.scala-lang.org/archives/downloads/distrib/files/nightly/pdfs/ScalaReference.pdf

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

4
ответ дан 28 November 2019 в 05:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: