From 7aade8fe3d805d3640a99a541f463fe894152b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 3 Sep 2025 17:51:12 +0200 Subject: [PATCH] Replace runtime.Rich* implicits on primitives by direct extension methods. It has always been weird that the public API of primitive types, enriched by `Predef` depends on the `Rich*` classes, which are in the not-really-public `runtime` package. Moreover, the design of those classes was overabstracted, with consequences on performance (unnecessary boxing) and quality of the API (including methods that do not make sense on some types). To mitigate that, the individual `Rich*` classes redefined some (but not all) of the methods, defeating the abstraction. We solve both issues with a simple solution: define all those methods as simple `extension` methods. We do this directly in the companion objects of the primitive types. --- library/src/scala/Boolean.scala | 12 +- library/src/scala/Byte.scala | 88 ++++++++++++- library/src/scala/Char.scala | 121 +++++++++++++++++- library/src/scala/Double.scala | 102 ++++++++++++++- library/src/scala/Float.scala | 101 ++++++++++++++- library/src/scala/Int.scala | 98 +++++++++++++- library/src/scala/Long.scala | 99 +++++++++++++- library/src/scala/Predef.scala | 35 +++-- library/src/scala/Short.scala | 87 ++++++++++++- library/src/scala/runtime/RichBoolean.scala | 1 + library/src/scala/runtime/RichByte.scala | 1 + library/src/scala/runtime/RichChar.scala | 1 + library/src/scala/runtime/RichDouble.scala | 1 + library/src/scala/runtime/RichFloat.scala | 1 + library/src/scala/runtime/RichInt.scala | 1 + library/src/scala/runtime/RichLong.scala | 1 + library/src/scala/runtime/RichShort.scala | 1 + .../src/scala/runtime/ScalaNumberProxy.scala | 21 +-- 18 files changed, 737 insertions(+), 35 deletions(-) diff --git a/library/src/scala/Boolean.scala b/library/src/scala/Boolean.scala index 5df9184b5e30..0e373ca537c9 100644 --- a/library/src/scala/Boolean.scala +++ b/library/src/scala/Boolean.scala @@ -138,5 +138,15 @@ object Boolean extends AnyValCompanion { /** The String representation of the scala.Boolean companion object. */ override def toString = "object scala.Boolean" + extension (self: Boolean) { + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this` is `true` and `that` is `false` + * - a negative value if `this` is `false` and `that` is `true` + * - `0` if `this == that` + */ + def compare(that: Boolean): Int = java.lang.Boolean.compare(self, that) + } } - diff --git a/library/src/scala/Byte.scala b/library/src/scala/Byte.scala index c1f0e81f746b..5d9c411ac984 100644 --- a/library/src/scala/Byte.scala +++ b/library/src/scala/Byte.scala @@ -18,6 +18,8 @@ package scala import scala.language.`2.13` +import scala.collection.immutable.NumericRange + /** `Byte`, a 8-bit signed integer (equivalent to Java's `byte` primitive type) is a * subtype of [[scala.AnyVal]]. Instances of `Byte` are not * represented by an object in the underlying runtime system. @@ -485,5 +487,89 @@ object Byte extends AnyValCompanion { implicit def byte2long(x: Byte): Long = x.toLong implicit def byte2float(x: Byte): Float = x.toFloat implicit def byte2double(x: Byte): Double = x.toDouble -} + extension (self: Byte) { + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `Byte`. + */ + @deprecated("isWhole on Byte is always true", "2.12.15") + def isWhole: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self>= 0 + + /** Returns `true` iff this is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidByte on Byte is always true", "3.8.0") + def isValidByte: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidShort on Byte is always true", "3.8.0") + def isValidShort: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidInt on Byte is always true", "3.8.0") + def isValidInt: Boolean = true + + /** Returns the absolute value of `this`. */ + def abs: Byte = java.lang.Math.abs(self.toInt).toByte + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Byte): Byte = java.lang.Math.max(self.toInt, that.toInt).toByte + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Byte): Byte = java.lang.Math.min(self.toInt, that.toInt).toByte + + /** Returns the sign of `this`. + * + * `0` if `this == 0`, `-1` if `this < 0` and `1` if `this> 0`. + */ + def sign: Byte = java.lang.Integer.signum(self.toInt).toByte + + /** Returns the signum of `this`. */ + @deprecated("use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign.toInt + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + */ + def compare(that: Byte): Int = java.lang.Byte.compare(self, that) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + */ + def until(end: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, 1) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def until(end: Byte, step: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, step) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + */ + def to(end: Byte): NumericRange.Inclusive[Byte] = NumericRange.inclusive(self, end, 1) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def to(end: Byte, step: Byte): NumericRange.Inclusive[Byte] = NumericRange.inclusive(self, end, step) + } +} diff --git a/library/src/scala/Char.scala b/library/src/scala/Char.scala index 06a6e186901b..7b1a3fcb9bd1 100644 --- a/library/src/scala/Char.scala +++ b/library/src/scala/Char.scala @@ -18,6 +18,8 @@ package scala import scala.language.`2.13` +import scala.collection.immutable.NumericRange + /** `Char`, a 16-bit unsigned integer (equivalent to Java's `char` primitive type) is a * subtype of [[scala.AnyVal]]. Instances of `Char` are not * represented by an object in the underlying runtime system. @@ -484,5 +486,122 @@ object Char extends AnyValCompanion { implicit def char2long(x: Char): Long = x.toLong implicit def char2float(x: Char): Float = x.toFloat implicit def char2double(x: Char): Double = x.toDouble -} + extension (self: Char) { + + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `RichInt`. + */ + @deprecated("isWhole on Char is always true", "2.12.15") + def isWhole: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidChar on Char is always true", "3.8.0") + def isValidChar: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toInt <= Byte.MaxValue.toInt + + /** Returns `true` iff this is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort: Boolean = self.toInt <= Short.MaxValue.toInt + + /** Returns `true` iff this is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidInt on Char is always true", "3.8.0") + def isValidInt: Boolean = true + + /** Returns the absolute value of `this`. */ + @deprecated("Char's are never negative; abs is redundant and can be removed", since = "3.8.0") + def abs: Char = self + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Char): Char = java.lang.Math.max(self.toInt, that.toInt).toChar + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Char): Char = java.lang.Math.min(self.toInt, that.toInt).toChar + + /** Returns the sign of `this`. + * + * zero if the argument is zero, -zero if the argument is -zero, + * one if the argument is greater than zero, -one if the argument is less than zero, + * and NaN if the argument is NaN where applicable. + */ + @deprecated("since Char's are never negative, compare to '\\u0000' instead", since = "3.8.0") + def sign: Char = java.lang.Integer.signum(self.toInt).toChar + + /** Returns the signum of `this`. */ + @deprecated("use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + */ + def compare(that: Char): Int = java.lang.Character.compare(self, that) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + */ + def until(end: Char): NumericRange.Exclusive[Char] = NumericRange(self, end, '\u0001') + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def until(end: Char, step: Char): NumericRange.Exclusive[Char] = NumericRange(self, end, step) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + */ + def to(end: Char): NumericRange.Inclusive[Char] = NumericRange.inclusive(self, end, '\u0001') + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def to(end: Char, step: Char): NumericRange.Inclusive[Char] = NumericRange.inclusive(self, end, step) + + def asDigit: Int = Character.digit(self, Character.MAX_RADIX) + + def isControl: Boolean = Character.isISOControl(self) + def isDigit: Boolean = Character.isDigit(self) + def isLetter: Boolean = Character.isLetter(self) + def isLetterOrDigit: Boolean = Character.isLetterOrDigit(self) + def isWhitespace: Boolean = Character.isWhitespace(self) + def isSpaceChar: Boolean = Character.isSpaceChar(self) + def isHighSurrogate: Boolean = Character.isHighSurrogate(self) + def isLowSurrogate: Boolean = Character.isLowSurrogate(self) + def isSurrogate: Boolean = isHighSurrogate || isLowSurrogate + def isUnicodeIdentifierStart: Boolean = Character.isUnicodeIdentifierStart(self) + def isUnicodeIdentifierPart: Boolean = Character.isUnicodeIdentifierPart(self) + def isIdentifierIgnorable: Boolean = Character.isIdentifierIgnorable(self) + def isMirrored: Boolean = Character.isMirrored(self) + + def isLower: Boolean = Character.isLowerCase(self) + def isUpper: Boolean = Character.isUpperCase(self) + def isTitleCase: Boolean = Character.isTitleCase(self) + + def toLower: Char = Character.toLowerCase(self) + def toUpper: Char = Character.toUpperCase(self) + def toTitleCase: Char = Character.toTitleCase(self) + + def getType: Int = Character.getType(self) + def getNumericValue: Int = Character.getNumericValue(self) + def getDirectionality: Byte = Character.getDirectionality(self) + def reverseBytes: Char = Character.reverseBytes(self) + } +} diff --git a/library/src/scala/Double.scala b/library/src/scala/Double.scala index 57e99999198b..42bae1c279eb 100644 --- a/library/src/scala/Double.scala +++ b/library/src/scala/Double.scala @@ -18,6 +18,8 @@ package scala import scala.language.`2.13` +import scala.collection.immutable.NumericRange + /** `Double`, a 64-bit IEEE-754 floating point number (equivalent to Java's `double` primitive type) is a * subtype of [[scala.AnyVal]]. Instances of `Double` are not * represented by an object in the underlying runtime system. @@ -253,5 +255,103 @@ object Double extends AnyValCompanion { /** The String representation of the scala.Double companion object. */ override def toString = "object scala.Double" -} + extension (self: Double) { + /** Returns `'''true'''` if this number is finite and has no decimal component. */ + def isWhole: Boolean = { + val l = self.toLong + l.toDouble == self || l == Long.MaxValue && self < Double.PositiveInfinity || l == Long.MinValue && self> Double.NegativeInfinity + } + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self.toChar.toDouble == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toByte.toDouble == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort: Boolean = self.toShort.toDouble == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidInt: Boolean = self.toInt.toDouble == self + + /** Returns `true` iff `this` is a `NaN` value. */ + def isNaN: Boolean = java.lang.Double.isNaN(self) + + /** Returns `true` iff `this` is `PositiveInfinity` or `NegativeInfinity`. */ + def isInfinity: Boolean = java.lang.Double.isInfinite(self) + + /** Returns `true` iff `this` is a finite value, i.e., not an infinity nor `NaN`. */ + def isFinite: Boolean = java.lang.Double.isFinite(self) + + /** Returns `true` iff `this` is `PositiveInfinity`. */ + def isPosInfinity: Boolean = Double.PositiveInfinity == self + + /** Returns `true` iff `this` is `NegativeInfinity`. */ + def isNegInfinity: Boolean = Double.NegativeInfinity == self + + /** Returns the absolute value of `this`. */ + def abs: Double = java.lang.Math.abs(self) + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Double): Double = java.lang.Math.max(self, that) + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Double): Double = java.lang.Math.min(self, that) + + /** Returns the sign of `this`. + * + * - `1.0` if `this> 0.0`; + * - `-1.0` if `this < 0.0`; + * - `this` otherwise (for zeros and `NaN`). + */ + def sign: Double = java.lang.Math.signum(self) + + /** Returns the signum of `this`. */ + @deprecated("signum does not handle -0.0 or Double.NaN; use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign.toInt + + /** Returns the closest `Long` to `this`. */ + def round: Long = java.lang.Math.round(self) + + /** Returns the smallest integer greater or equal to `this`. */ + def ceil: Double = java.lang.Math.ceil(self) + + /** Returns the largest integer smaller or equal to `this`. */ + def floor: Double = java.lang.Math.floor(self) + + /** Converts an angle measured in degrees to an approximately equivalent + * angle measured in radians. + * + * @return the measurement of the angle x in radians. + */ + def toRadians: Double = java.lang.Math.toRadians(self) + + /** Converts an angle measured in radians to an approximately equivalent + * angle measured in degrees. + * @return the measurement of the angle x in degrees. + */ + def toDegrees: Double = java.lang.Math.toDegrees(self) + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + * + * Special cases for this method: + * - `0.0` is considered greater than `-0.0` + * - `NaN` is considered greater than all other values, but equal to itself + */ + def compare(that: Double): Int = java.lang.Double.compare(self, that) + } +} diff --git a/library/src/scala/Float.scala b/library/src/scala/Float.scala index 1f8b00886258..975e6fbfb00b 100644 --- a/library/src/scala/Float.scala +++ b/library/src/scala/Float.scala @@ -256,5 +256,104 @@ object Float extends AnyValCompanion { /** Language mandated coercions from Float to "wider" types. */ import scala.language.implicitConversions implicit def float2double(x: Float): Double = x.toDouble -} + extension (self: Float) { + /** Returns `'''true'''` if this number is finite and has no decimal component. */ + def isWhole: Boolean = { + val i = self.toInt + i.toFloat == self || i == Int.MaxValue && self < Float.PositiveInfinity || i == Int.MinValue && self> Float.NegativeInfinity + } + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self.toChar.toFloat == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toByte.toFloat == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort: Boolean = self.toShort.toFloat == self + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidInt: Boolean = + self.toInt.toFloat == self && self>= Int.MinValue.toFloat && self < -Int.MinValue.toFloat + + /** Returns `true` iff `this` is a `NaN` value. */ + def isNaN: Boolean = java.lang.Float.isNaN(self) + + /** Returns `true` iff `this` is `PositiveInfinity` or `NegativeInfinity`. */ + def isInfinity: Boolean = java.lang.Float.isInfinite(self) + + /** Returns `true` iff `this` is a finite value, i.e., not an infinity nor `NaN`. */ + def isFinite: Boolean = java.lang.Float.isFinite(self) + + /** Returns `true` iff `this` is `PositiveInfinity`. */ + def isPosInfinity: Boolean = Float.PositiveInfinity == self + + /** Returns `true` iff `this` is `NegativeInfinity`. */ + def isNegInfinity: Boolean = Float.NegativeInfinity == self + + /** Returns the absolute value of `this`. */ + def abs: Float = java.lang.Math.abs(self) + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Float): Float = java.lang.Math.max(self, that) + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Float): Float = java.lang.Math.min(self, that) + + /** Returns the sign of `this`. + * + * - `1.0f` if `this> 0.0f`; + * - `-1.0f` if `this < 0.0f`; + * - `this` otherwise (for zeros and `NaN`). + */ + def sign: Float = java.lang.Math.signum(self) + + /** Returns the signum of `this`. */ + @deprecated("signum does not handle -0.0f or Double.NaN; use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign.toInt + + /** Returns the closest `Int` to `this`. */ + def round: Int = java.lang.Math.round(self) + + /** Returns the smallest integer greater or equal to `this`. */ + def ceil: Float = java.lang.Math.ceil(self.toDouble).toFloat + + /** Returns the largest integer smaller or equal to `this`. */ + def floor: Float = java.lang.Math.floor(self.toDouble).toFloat + + /** Converts an angle measured in degrees to an approximately equivalent + * angle measured in radians. + * + * @return the measurement of the angle x in radians. + */ + def toRadians: Float = java.lang.Math.toRadians(self.toDouble).toFloat + + /** Converts an angle measured in radians to an approximately equivalent + * angle measured in degrees. + * @return the measurement of the angle x in degrees. + */ + def toDegrees: Float = java.lang.Math.toDegrees(self.toDouble).toFloat + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + * + * Special cases for this method: + * - `0.0` is considered greater than `-0.0` + * - `NaN` is considered greater than all other values, but equal to itself + */ + def compare(that: Float): Int = java.lang.Float.compare(self, that) + } +} diff --git a/library/src/scala/Int.scala b/library/src/scala/Int.scala index a3fff7e991da..28e40a26de19 100644 --- a/library/src/scala/Int.scala +++ b/library/src/scala/Int.scala @@ -484,5 +484,101 @@ object Int extends AnyValCompanion { implicit def int2float(x: Int): Float = x.toFloat implicit def int2long(x: Int): Long = x.toLong implicit def int2double(x: Int): Double = x.toDouble -} + extension (self: Int) { + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `Int`. + */ + @deprecated("isWhole on Int is always true", "2.12.15") + def isWhole: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self.toChar.toInt == self + + /** Returns `true` iff this is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toByte.toInt == self + + /** Returns `true` iff this is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort: Boolean = self.toShort.toInt == self + + /** Returns `true` iff this is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidInt on Int is always true", "3.8.0") + def isValidInt: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Long]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidLong on Int is always true", "3.8.0") + def isValidLong: Boolean = true + + /** Returns the absolute value of `this`. */ + def abs: Int = java.lang.Math.abs(self) + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Int): Int = java.lang.Math.max(self, that) + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Int): Int = java.lang.Math.min(self, that) + + /** Returns the sign of `this`. + * + * `0` if `this == 0`, `-1` if `this < 0` and `1` if `this> 0`. + */ + def sign: Int = java.lang.Integer.signum(self) + + /** Returns the signum of `this`. */ + @deprecated("use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign + + /** There is no reason to round an `Int`, but this method is provided to avoid accidental loss of precision from a detour through `Float`. */ + @deprecated("this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Int = self + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + */ + def compare(that: Int): Int = java.lang.Integer.compare(self, that) + + def toBinaryString: String = java.lang.Integer.toBinaryString(self) + def toHexString: String = java.lang.Integer.toHexString(self) + def toOctalString: String = java.lang.Integer.toOctalString(self) + + /** A [[scala.collection.immutable.Range]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + */ + def until(end: Int): Range = Range(self, end) + + /** A [[scala.collection.immutable.Range]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def until(end: Int, step: Int): Range = Range(self, end, step) + + /** A [[scala.collection.immutable.Range]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + */ + def to(end: Int): Range.Inclusive = Range.inclusive(self, end) + + /** A [[scala.collection.immutable.Range]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def to(end: Int, step: Int): Range.Inclusive = Range.inclusive(self, end, step) + } +} diff --git a/library/src/scala/Long.scala b/library/src/scala/Long.scala index 2561c1ec74c2..6d1e1f29ea6b 100644 --- a/library/src/scala/Long.scala +++ b/library/src/scala/Long.scala @@ -18,6 +18,8 @@ package scala import scala.language.`2.13` +import scala.collection.immutable.NumericRange + /** `Long`, a 64-bit signed integer (equivalent to Java's `long` primitive type) is a * subtype of [[scala.AnyVal]]. Instances of `Long` are not * represented by an object in the underlying runtime system. @@ -481,5 +483,100 @@ object Long extends AnyValCompanion { implicit def long2float(x: Long): Float = x.toFloat @deprecated("Implicit conversion from Long to Double is dangerous because it loses precision. Write `.toDouble` instead.", "2.13.1") implicit def long2double(x: Long): Double = x.toDouble -} + extension (self: Long) { + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `Long`. + */ + @deprecated("isWhole on Long is always true", "2.12.15") + def isWhole: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self.toChar.toLong == self + + /** Returns `true` iff this is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toByte.toLong == self + + /** Returns `true` iff this is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort: Boolean = self.toShort.toLong == self + + /** Returns `true` iff this is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidInt: Boolean = self.toShort.toLong == self + + /** Returns `true` iff this is within the + * range of [[scala.Long]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidLong on Long is always true", "3.8.0") + def isValidLong: Boolean = true + + /** Returns the absolute value of `this`. */ + def abs: Long = java.lang.Math.abs(self) + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Long): Long = java.lang.Math.max(self, that) + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Long): Long = java.lang.Math.min(self, that) + + /** Returns the sign of `this`. + * + * `0` if `this == 0`, `-1` if `this < 0` and `1` if `this> 0`. + */ + def sign: Long = java.lang.Long.signum(self) + + /** Returns the signum of `this`. */ + @deprecated("use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign.toInt + + /** There is no reason to round an `Int`, but this method is provided to avoid accidental loss of precision from a detour through `Double`. */ + @deprecated("this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Long = self + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + */ + def compare(that: Long): Int = java.lang.Long.compare(self, that) + + def toBinaryString: String = java.lang.Long.toBinaryString(self) + def toHexString: String = java.lang.Long.toHexString(self) + def toOctalString: String = java.lang.Long.toOctalString(self) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + */ + def until(end: Long): NumericRange.Exclusive[Long] = NumericRange(self, end, 1L) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def until(end: Long, step: Long): NumericRange.Exclusive[Long] = NumericRange(self, end, step) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + */ + def to(end: Long): NumericRange.Inclusive[Long] = NumericRange.inclusive(self, end, 1L) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def to(end: Long, step: Long): NumericRange.Inclusive[Long] = NumericRange.inclusive(self, end, step) + } +} diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala index 552a491470ae..16c9af8f4688 100644 --- a/library/src/scala/Predef.scala +++ b/library/src/scala/Predef.scala @@ -637,24 +637,23 @@ object Predef extends LowPriorityImplicits { private[scala] abstract class LowPriorityImplicits extends LowPriorityImplicits2 { import mutable.ArraySeq - /** We prefer the java.lang.* boxed types to these wrappers in - * any potential conflicts. Conflicts do exist because the wrappers - * need to implement ScalaNumber in order to have a symmetric equals - * method, but that implies implementing java.lang.Number as well. - * - * Note - these are inlined because they are value classes, but - * the call to xxxWrapper is not eliminated even though it does nothing. - * Even inlined, every call site does a no-op retrieval of Predef's MODULE$ - * because maybe loading Predef has side effects! - */ - @inline implicit def byteWrapper(x: Byte): runtime.RichByte = new runtime.RichByte(x) - @inline implicit def shortWrapper(x: Short): runtime.RichShort = new runtime.RichShort(x) - @inline implicit def intWrapper(x: Int): runtime.RichInt = new runtime.RichInt(x) - @inline implicit def charWrapper(c: Char): runtime.RichChar = new runtime.RichChar(c) - @inline implicit def longWrapper(x: Long): runtime.RichLong = new runtime.RichLong(x) - @inline implicit def floatWrapper(x: Float): runtime.RichFloat = new runtime.RichFloat(x) - @inline implicit def doubleWrapper(x: Double): runtime.RichDouble = new runtime.RichDouble(x) - @inline implicit def booleanWrapper(x: Boolean): runtime.RichBoolean = new runtime.RichBoolean(x) + // Deprecated conversions to runtime.Rich* classes; these methods used to be `implicit` + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def byteWrapper(x: Byte): runtime.RichByte = new runtime.RichByte(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def shortWrapper(x: Short): runtime.RichShort = new runtime.RichShort(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def intWrapper(x: Int): runtime.RichInt = new runtime.RichInt(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def charWrapper(c: Char): runtime.RichChar = new runtime.RichChar(c) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def longWrapper(x: Long): runtime.RichLong = new runtime.RichLong(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def floatWrapper(x: Float): runtime.RichFloat = new runtime.RichFloat(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def doubleWrapper(x: Double): runtime.RichDouble = new runtime.RichDouble(x) + @deprecated("use the extension methods available on primitive types instead", since = "3.8.0") + @inline def booleanWrapper(x: Boolean): runtime.RichBoolean = new runtime.RichBoolean(x) /** @group conversions-array-to-wrapped-array */ implicit def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = diff --git a/library/src/scala/Short.scala b/library/src/scala/Short.scala index 03b0929ea5ce..ac16fc1eef84 100644 --- a/library/src/scala/Short.scala +++ b/library/src/scala/Short.scala @@ -18,6 +18,8 @@ package scala import scala.language.`2.13` +import scala.collection.immutable.NumericRange + /** `Short`, a 16-bit signed integer (equivalent to Java's `short` primitive type) is a * subtype of [[scala.AnyVal]]. Instances of `Short` are not * represented by an object in the underlying runtime system. @@ -484,5 +486,88 @@ object Short extends AnyValCompanion { implicit def short2long(x: Short): Long = x.toLong implicit def short2float(x: Short): Float = x.toFloat implicit def short2double(x: Short): Double = x.toDouble -} + extension (self: Short) { + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `Short`. + */ + @deprecated("isWhole on Short is always true", "2.12.15") + def isWhole: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar: Boolean = self>= 0 + + /** Returns `true` iff this is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte: Boolean = self.toByte.toInt == self.toInt + + /** Returns `true` iff this is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidShort on Short is always true", "3.8.0") + def isValidShort: Boolean = true + + /** Returns `true` iff this is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + @deprecated("isValidInt on Short is always true", "3.8.0") + def isValidInt: Boolean = true + + /** Returns the absolute value of `this`. */ + def abs: Short = java.lang.Math.abs(self.toInt).toShort + + /** Returns `this` if `this> that` or `that` otherwise. */ + def max(that: Short): Short = java.lang.Math.max(self.toInt, that.toInt).toShort + + /** Returns `this` if `this < that` or `that` otherwise. */ + def min(that: Short): Short = java.lang.Math.min(self.toInt, that.toInt).toShort + + /** Returns the sign of `this`. + * + * `0` if `this == 0`, `-1` if `this < 0` and `1` if `this> 0`. + */ + def sign: Short = java.lang.Integer.signum(self.toInt).toShort + + /** Returns the signum of `this`. */ + @deprecated("use `sign` method instead", since = "2.13.0") + def signum: Int = self.sign.toInt + + /** Compares `this` to `that` according to the standard total ordering. + * + * Returns: + * - a positive value if `this> that` + * - a negative value if `this < that` + * - `0` if `this == that` + */ + def compare(that: Short): Int = java.lang.Short.compare(self, that) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + */ + def until(end: Short): NumericRange.Exclusive[Short] = NumericRange(self, end, 1) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def until(end: Short, step: Short): NumericRange.Exclusive[Short] = NumericRange(self, end, step) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + */ + def to(end: Short): NumericRange.Inclusive[Short] = NumericRange.inclusive(self, end, 1) + + /** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`. + * + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + */ + def to(end: Short, step: Short): NumericRange.Inclusive[Short] = NumericRange.inclusive(self, end, step) + } +} diff --git a/library/src/scala/runtime/RichBoolean.scala b/library/src/scala/runtime/RichBoolean.scala index 9b7745f9859a..42131b60596d 100644 --- a/library/src/scala/runtime/RichBoolean.scala +++ b/library/src/scala/runtime/RichBoolean.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichBoolean(val self: Boolean) extends AnyVal with OrderedProxy[Boolean] { protected def ord: scala.math.Ordering.Boolean.type = scala.math.Ordering.Boolean } diff --git a/library/src/scala/runtime/RichByte.scala b/library/src/scala/runtime/RichByte.scala index 58b68b21b31b..251b665269de 100644 --- a/library/src/scala/runtime/RichByte.scala +++ b/library/src/scala/runtime/RichByte.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichByte(val self: Byte) extends AnyVal with ScalaWholeNumberProxy[Byte] { protected def num: scala.math.Numeric.ByteIsIntegral.type = scala.math.Numeric.ByteIsIntegral protected def ord: scala.math.Ordering.Byte.type = scala.math.Ordering.Byte diff --git a/library/src/scala/runtime/RichChar.scala b/library/src/scala/runtime/RichChar.scala index 8bdda656f987..257e6700a0ed 100644 --- a/library/src/scala/runtime/RichChar.scala +++ b/library/src/scala/runtime/RichChar.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichChar(val self: Char) extends AnyVal with IntegralProxy[Char] { protected def num: scala.math.Numeric.CharIsIntegral.type = scala.math.Numeric.CharIsIntegral protected def ord: scala.math.Ordering.Char.type = scala.math.Ordering.Char diff --git a/library/src/scala/runtime/RichDouble.scala b/library/src/scala/runtime/RichDouble.scala index a1b8bdd065fa..aaec3da6d463 100644 --- a/library/src/scala/runtime/RichDouble.scala +++ b/library/src/scala/runtime/RichDouble.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichDouble(val self: Double) extends AnyVal with FractionalProxy[Double] { protected def num: Fractional[Double] = scala.math.Numeric.DoubleIsFractional protected def ord: Ordering[Double] = scala.math.Ordering.Double.TotalOrdering diff --git a/library/src/scala/runtime/RichFloat.scala b/library/src/scala/runtime/RichFloat.scala index 78cabf5e7402..463980dd8778 100644 --- a/library/src/scala/runtime/RichFloat.scala +++ b/library/src/scala/runtime/RichFloat.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichFloat(val self: Float) extends AnyVal with FractionalProxy[Float] { protected def num: Fractional[Float] = scala.math.Numeric.FloatIsFractional protected def ord: Ordering[Float] = scala.math.Ordering.Float.TotalOrdering diff --git a/library/src/scala/runtime/RichInt.scala b/library/src/scala/runtime/RichInt.scala index 10d2d3b9ea0e..a7176cafd290 100644 --- a/library/src/scala/runtime/RichInt.scala +++ b/library/src/scala/runtime/RichInt.scala @@ -18,6 +18,7 @@ import scala.collection.immutable.Range // Note that this does not implement IntegralProxy[Int] so that it can return // the Int-specific Range class from until/to. +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] with RangedProxy[Int] { protected def num: scala.math.Numeric.IntIsIntegral.type = scala.math.Numeric.IntIsIntegral protected def ord: scala.math.Ordering.Int.type = scala.math.Ordering.Int diff --git a/library/src/scala/runtime/RichLong.scala b/library/src/scala/runtime/RichLong.scala index 2dc7d6ffa2d4..f9ae309e389f 100644 --- a/library/src/scala/runtime/RichLong.scala +++ b/library/src/scala/runtime/RichLong.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichLong(val self: Long) extends AnyVal with IntegralProxy[Long] { protected def num: scala.math.Numeric.LongIsIntegral.type = scala.math.Numeric.LongIsIntegral protected def ord: scala.math.Ordering.Long.type = scala.math.Ordering.Long diff --git a/library/src/scala/runtime/RichShort.scala b/library/src/scala/runtime/RichShort.scala index 31f189380f94..3062fcc43c72 100644 --- a/library/src/scala/runtime/RichShort.scala +++ b/library/src/scala/runtime/RichShort.scala @@ -15,6 +15,7 @@ package runtime import scala.language.`2.13` +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") final class RichShort(val self: Short) extends AnyVal with ScalaWholeNumberProxy[Short] { protected def num: scala.math.Numeric.ShortIsIntegral.type = scala.math.Numeric.ShortIsIntegral protected def ord: scala.math.Ordering.Short.type = scala.math.Ordering.Short diff --git a/library/src/scala/runtime/ScalaNumberProxy.scala b/library/src/scala/runtime/ScalaNumberProxy.scala index cf70558469de..6a9845d1f54c 100644 --- a/library/src/scala/runtime/ScalaNumberProxy.scala +++ b/library/src/scala/runtime/ScalaNumberProxy.scala @@ -17,14 +17,12 @@ import scala.language.`2.13` import scala.collection.immutable import scala.math.ScalaNumericAnyConversions import immutable.NumericRange -import Proxy.Typed -import scala.annotation.nowarn /** Base classes for the Rich* wrappers of the primitive types. * As with all classes in scala.runtime.*, this is not a supported API. */ -@nowarn("cat=deprecation") -trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed[T] with OrderedProxy[T] { +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") +trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Proxy.Typed[T] with OrderedProxy[T] { protected implicit def num: Numeric[T] def doubleValue = num.toDouble(self) @@ -50,10 +48,14 @@ trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed /** Returns the signum of `'''this'''`. */ @deprecated("use `sign` method instead", since = "2.13.0") def signum: Int = num.signum(self) } + +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") trait ScalaWholeNumberProxy[T] extends Any with ScalaNumberProxy[T] { @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole = true } + +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProxy[T] { protected implicit def num: Integral[T] type ResultWithoutStep = NumericRange[T] @@ -63,21 +65,23 @@ trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProx def to(end: T): NumericRange.Inclusive[T] = NumericRange.inclusive(self, end, num.one) def to(end: T, step: T): NumericRange.Inclusive[T] = NumericRange.inclusive(self, end, step) } + +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") trait FractionalProxy[T] extends Any with ScalaNumberProxy[T] { protected implicit def num: Fractional[T] def isWhole = false } -@nowarn("cat=deprecation") -trait OrderedProxy[T] extends Any with Ordered[T] with Typed[T] { +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") +trait OrderedProxy[T] extends Any with Ordered[T] with Proxy.Typed[T] { protected def ord: Ordering[T] def compare(y: T) = ord.compare(self, y) } -@nowarn("cat=deprecation") -trait RangedProxy[T] extends Any with Typed[T] { +@deprecated("use the extension methods available on primitive types instead", since = "3.8.0") +trait RangedProxy[T] extends Any with Proxy.Typed[T] { type ResultWithoutStep def until(end: T): ResultWithoutStep @@ -85,4 +89,3 @@ trait RangedProxy[T] extends Any with Typed[T] { def to(end: T): ResultWithoutStep def to(end: T, step: T): immutable.IndexedSeq[T] } -

AltStyle によって変換されたページ (->オリジナル) /