Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit eb383fe

Browse files
committed
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.
1 parent eb1bb73 commit eb383fe

22 files changed

+765
-59
lines changed

‎library/src/scala/Boolean.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,27 @@ object Boolean extends AnyValCompanion {
138138
/** The String representation of the scala.Boolean companion object. */
139139
override def toString = "object scala.Boolean"
140140

141-
}
141+
extension (self: Boolean) {
142+
143+
/** Compares `this` to `that` according to the standard total ordering.
144+
*
145+
* Returns:
146+
* - a positive value if `this` is `true` and `that` is `false`
147+
* - a negative value if `this` is `false` and `that` is `true`
148+
* - `0` if `this == that`
149+
*/
150+
def compare(that: Boolean): Int = java.lang.Boolean.compare(self, that)
151+
152+
/** Returns true iff `this` is `false` and `that` is `true`. */
153+
def <(that: Boolean): Boolean = !self & that
142154

155+
/** Returns true iff `this` is `true` and `that` is `false`. */
156+
def >(that: Boolean): Boolean = self & !that
157+
158+
/** Returns true iff `this` is `false` or `that` is `true`. */
159+
def <=(that: Boolean): Boolean = !self | that
160+
161+
/** Returns true iff `this` is `true` or `that` is `false`. */
162+
def >=(that: Boolean): Boolean = self | !that
163+
}
164+
}

‎library/src/scala/Byte.scala

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package scala
1818

1919
import scala.language.`2.13`
2020

21+
import scala.collection.immutable.NumericRange
22+
2123
/** `Byte`, a 8-bit signed integer (equivalent to Java's `byte` primitive type) is a
2224
* subtype of [[scala.AnyVal]]. Instances of `Byte` are not
2325
* represented by an object in the underlying runtime system.
@@ -485,5 +487,89 @@ object Byte extends AnyValCompanion {
485487
implicit def byte2long(x: Byte): Long = x.toLong
486488
implicit def byte2float(x: Byte): Float = x.toFloat
487489
implicit def byte2double(x: Byte): Double = x.toDouble
488-
}
489490

491+
extension (self: Byte) {
492+
/** Returns `'''true'''` if this number has no decimal component.
493+
* Always `'''true'''` for `Byte`.
494+
*/
495+
@deprecated("isWhole on Byte is always true", "2.12.15")
496+
def isWhole: Boolean = true
497+
498+
/** Returns `true` iff this is within the
499+
* range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`.
500+
*/
501+
def isValidChar: Boolean = self >= 0
502+
503+
/** Returns `true` iff this is within the
504+
* range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`.
505+
*/
506+
@deprecated("isValidByte on Byte is always true", "3.8.0")
507+
def isValidByte: Boolean = true
508+
509+
/** Returns `true` iff this is within the
510+
* range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`.
511+
*/
512+
@deprecated("isValidShort on Byte is always true", "3.8.0")
513+
def isValidShort: Boolean = true
514+
515+
/** Returns `true` iff this is within the
516+
* range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`.
517+
*/
518+
@deprecated("isValidInt on Byte is always true", "3.8.0")
519+
def isValidInt: Boolean = true
520+
521+
/** Returns the absolute value of `this`. */
522+
def abs: Byte = java.lang.Math.abs(self.toInt).toByte
523+
524+
/** Returns `this` if `this > that` or `that` otherwise. */
525+
def max(that: Byte): Byte = java.lang.Math.max(self.toInt, that.toInt).toByte
526+
527+
/** Returns `this` if `this < that` or `that` otherwise. */
528+
def min(that: Byte): Byte = java.lang.Math.min(self.toInt, that.toInt).toByte
529+
530+
/** Returns the sign of `this`.
531+
*
532+
* `0` if `this == 0`, `-1` if `this < 0` and `1` if `this > 0`.
533+
*/
534+
def sign: Byte = java.lang.Integer.signum(self.toInt).toByte
535+
536+
/** Returns the signum of `this`. */
537+
@deprecated("use `sign` method instead", since = "2.13.0")
538+
def signum: Int = self.sign.toInt
539+
540+
/** Compares `this` to `that` according to the standard total ordering.
541+
*
542+
* Returns:
543+
* - a positive value if `this > that`
544+
* - a negative value if `this < that`
545+
* - `0` if `this == that`
546+
*/
547+
def compare(that: Byte): Int = java.lang.Byte.compare(self, that)
548+
549+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`.
550+
*
551+
* @param end The final bound of the range to make.
552+
*/
553+
def until(end: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, 1)
554+
555+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`.
556+
*
557+
* @param end The final bound of the range to make.
558+
* @param step The number to increase by for each step of the range.
559+
*/
560+
def until(end: Byte, step: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, step)
561+
562+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`.
563+
*
564+
* @param end The final bound of the range to make.
565+
*/
566+
def to(end: Byte): NumericRange.Inclusive[Byte] = NumericRange.inclusive(self, end, 1)
567+
568+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`.
569+
*
570+
* @param end The final bound of the range to make.
571+
* @param step The number to increase by for each step of the range.
572+
*/
573+
def to(end: Byte, step: Byte): NumericRange.Inclusive[Byte] = NumericRange.inclusive(self, end, step)
574+
}
575+
}

‎library/src/scala/Char.scala

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package scala
1818

1919
import scala.language.`2.13`
2020

21+
import scala.collection.immutable.NumericRange
22+
2123
/** `Char`, a 16-bit unsigned integer (equivalent to Java's `char` primitive type) is a
2224
* subtype of [[scala.AnyVal]]. Instances of `Char` are not
2325
* represented by an object in the underlying runtime system.
@@ -484,5 +486,122 @@ object Char extends AnyValCompanion {
484486
implicit def char2long(x: Char): Long = x.toLong
485487
implicit def char2float(x: Char): Float = x.toFloat
486488
implicit def char2double(x: Char): Double = x.toDouble
487-
}
488489

490+
extension (self: Char) {
491+
492+
/** Returns `'''true'''` if this number has no decimal component.
493+
* Always `'''true'''` for `RichInt`.
494+
*/
495+
@deprecated("isWhole on Char is always true", "2.12.15")
496+
def isWhole: Boolean = true
497+
498+
/** Returns `true` iff this is within the
499+
* range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`.
500+
*/
501+
@deprecated("isValidChar on Char is always true", "3.8.0")
502+
def isValidChar: Boolean = true
503+
504+
/** Returns `true` iff this is within the
505+
* range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`.
506+
*/
507+
def isValidByte: Boolean = self.toInt <= Byte.MaxValue.toInt
508+
509+
/** Returns `true` iff this is within the
510+
* range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`.
511+
*/
512+
def isValidShort: Boolean = self.toInt <= Short.MaxValue.toInt
513+
514+
/** Returns `true` iff this is within the
515+
* range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`.
516+
*/
517+
@deprecated("isValidInt on Char is always true", "3.8.0")
518+
def isValidInt: Boolean = true
519+
520+
/** Returns the absolute value of `this`. */
521+
@deprecated("Char's are never negative; abs is redundant and can be removed", since = "3.8.0")
522+
def abs: Char = self
523+
524+
/** Returns `this` if `this > that` or `that` otherwise. */
525+
def max(that: Char): Char = java.lang.Math.max(self.toInt, that.toInt).toChar
526+
527+
/** Returns `this` if `this < that` or `that` otherwise. */
528+
def min(that: Char): Char = java.lang.Math.min(self.toInt, that.toInt).toChar
529+
530+
/** Returns the sign of `this`.
531+
*
532+
* zero if the argument is zero, -zero if the argument is -zero,
533+
* one if the argument is greater than zero, -one if the argument is less than zero,
534+
* and NaN if the argument is NaN where applicable.
535+
*/
536+
@deprecated("since Char's are never negative, compare to '\\u0000' instead", since = "3.8.0")
537+
def sign: Char = java.lang.Integer.signum(self.toInt).toChar
538+
539+
/** Returns the signum of `this`. */
540+
@deprecated("use `sign` method instead", since = "2.13.0")
541+
def signum: Int = self.sign
542+
543+
/** Compares `this` to `that` according to the standard total ordering.
544+
*
545+
* Returns:
546+
* - a positive value if `this > that`
547+
* - a negative value if `this < that`
548+
* - `0` if `this == that`
549+
*/
550+
def compare(that: Char): Int = java.lang.Character.compare(self, that)
551+
552+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`.
553+
*
554+
* @param end The final bound of the range to make.
555+
*/
556+
def until(end: Char): NumericRange.Exclusive[Char] = NumericRange(self, end, '\u0001')
557+
558+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to but not including `end`.
559+
*
560+
* @param end The final bound of the range to make.
561+
* @param step The number to increase by for each step of the range.
562+
*/
563+
def until(end: Char, step: Char): NumericRange.Exclusive[Char] = NumericRange(self, end, step)
564+
565+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`.
566+
*
567+
* @param end The final bound of the range to make.
568+
*/
569+
def to(end: Char): NumericRange.Inclusive[Char] = NumericRange.inclusive(self, end, '\u0001')
570+
571+
/** A [[scala.collection.immutable.NumericRange]] from `this` up to and including `end`.
572+
*
573+
* @param end The final bound of the range to make.
574+
* @param step The number to increase by for each step of the range.
575+
*/
576+
def to(end: Char, step: Char): NumericRange.Inclusive[Char] = NumericRange.inclusive(self, end, step)
577+
578+
def asDigit: Int = Character.digit(self, Character.MAX_RADIX)
579+
580+
def isControl: Boolean = Character.isISOControl(self)
581+
def isDigit: Boolean = Character.isDigit(self)
582+
def isLetter: Boolean = Character.isLetter(self)
583+
def isLetterOrDigit: Boolean = Character.isLetterOrDigit(self)
584+
def isWhitespace: Boolean = Character.isWhitespace(self)
585+
def isSpaceChar: Boolean = Character.isSpaceChar(self)
586+
def isHighSurrogate: Boolean = Character.isHighSurrogate(self)
587+
def isLowSurrogate: Boolean = Character.isLowSurrogate(self)
588+
def isSurrogate: Boolean = isHighSurrogate || isLowSurrogate
589+
def isUnicodeIdentifierStart: Boolean = Character.isUnicodeIdentifierStart(self)
590+
def isUnicodeIdentifierPart: Boolean = Character.isUnicodeIdentifierPart(self)
591+
def isIdentifierIgnorable: Boolean = Character.isIdentifierIgnorable(self)
592+
def isMirrored: Boolean = Character.isMirrored(self)
593+
594+
def isLower: Boolean = Character.isLowerCase(self)
595+
def isUpper: Boolean = Character.isUpperCase(self)
596+
def isTitleCase: Boolean = Character.isTitleCase(self)
597+
598+
def toLower: Char = Character.toLowerCase(self)
599+
def toUpper: Char = Character.toUpperCase(self)
600+
def toTitleCase: Char = Character.toTitleCase(self)
601+
602+
def getType: Int = Character.getType(self)
603+
def getNumericValue: Int = Character.getNumericValue(self)
604+
def getDirectionality: Byte = Character.getDirectionality(self)
605+
def reverseBytes: Char = Character.reverseBytes(self)
606+
}
607+
}

‎library/src/scala/Double.scala

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package scala
1818

1919
import scala.language.`2.13`
2020

21+
import scala.collection.immutable.NumericRange
22+
2123
/** `Double`, a 64-bit IEEE-754 floating point number (equivalent to Java's `double` primitive type) is a
2224
* subtype of [[scala.AnyVal]]. Instances of `Double` are not
2325
* represented by an object in the underlying runtime system.
@@ -253,5 +255,103 @@ object Double extends AnyValCompanion {
253255

254256
/** The String representation of the scala.Double companion object. */
255257
override def toString = "object scala.Double"
256-
}
257258

259+
extension (self: Double) {
260+
/** Returns `'''true'''` if this number is finite and has no decimal component. */
261+
def isWhole: Boolean = {
262+
val l = self.toLong
263+
l.toDouble == self || l == Long.MaxValue && self < Double.PositiveInfinity || l == Long.MinValue && self > Double.NegativeInfinity
264+
}
265+
266+
/** Returns `true` iff this has a zero fractional part, and is within the
267+
* range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`.
268+
*/
269+
def isValidChar: Boolean = self.toChar.toDouble == self
270+
271+
/** Returns `true` iff this has a zero fractional part, and is within the
272+
* range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`.
273+
*/
274+
def isValidByte: Boolean = self.toByte.toDouble == self
275+
276+
/** Returns `true` iff this has a zero fractional part, and is within the
277+
* range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`.
278+
*/
279+
def isValidShort: Boolean = self.toShort.toDouble == self
280+
281+
/** Returns `true` iff this has a zero fractional part, and is within the
282+
* range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`.
283+
*/
284+
def isValidInt: Boolean = self.toInt.toDouble == self
285+
286+
/** Returns `true` iff `this` is a `NaN` value. */
287+
def isNaN: Boolean = java.lang.Double.isNaN(self)
288+
289+
/** Returns `true` iff `this` is `PositiveInfinity` or `NegativeInfinity`. */
290+
def isInfinity: Boolean = java.lang.Double.isInfinite(self)
291+
292+
/** Returns `true` iff `this` is a finite value, i.e., not an infinity nor `NaN`. */
293+
def isFinite: Boolean = java.lang.Double.isFinite(self)
294+
295+
/** Returns `true` iff `this` is `PositiveInfinity`. */
296+
def isPosInfinity: Boolean = Double.PositiveInfinity == self
297+
298+
/** Returns `true` iff `this` is `NegativeInfinity`. */
299+
def isNegInfinity: Boolean = Double.NegativeInfinity == self
300+
301+
/** Returns the absolute value of `this`. */
302+
def abs: Double = java.lang.Math.abs(self)
303+
304+
/** Returns `this` if `this > that` or `that` otherwise. */
305+
def max(that: Double): Double = java.lang.Math.max(self, that)
306+
307+
/** Returns `this` if `this < that` or `that` otherwise. */
308+
def min(that: Double): Double = java.lang.Math.min(self, that)
309+
310+
/** Returns the sign of `this`.
311+
*
312+
* - `1.0` if `this > 0.0`;
313+
* - `-1.0` if `this < 0.0`;
314+
* - `this` otherwise (for zeros and `NaN`).
315+
*/
316+
def sign: Double = java.lang.Math.signum(self)
317+
318+
/** Returns the signum of `this`. */
319+
@deprecated("signum does not handle -0.0 or Double.NaN; use `sign` method instead", since = "2.13.0")
320+
def signum: Int = self.sign.toInt
321+
322+
/** Returns the closest `Long` to `this`. */
323+
def round: Long = java.lang.Math.round(self)
324+
325+
/** Returns the smallest integer greater or equal to `this`. */
326+
def ceil: Double = java.lang.Math.ceil(self)
327+
328+
/** Returns the largest integer smaller or equal to `this`. */
329+
def floor: Double = java.lang.Math.floor(self)
330+
331+
/** Converts an angle measured in degrees to an approximately equivalent
332+
* angle measured in radians.
333+
*
334+
* @return the measurement of the angle x in radians.
335+
*/
336+
def toRadians: Double = java.lang.Math.toRadians(self)
337+
338+
/** Converts an angle measured in radians to an approximately equivalent
339+
* angle measured in degrees.
340+
* @return the measurement of the angle x in degrees.
341+
*/
342+
def toDegrees: Double = java.lang.Math.toDegrees(self)
343+
344+
/** Compares `this` to `that` according to the standard total ordering.
345+
*
346+
* Returns:
347+
* - a positive value if `this > that`
348+
* - a negative value if `this < that`
349+
* - `0` if `this == that`
350+
*
351+
* Special cases for this method:
352+
* - `0.0` is considered greater than `-0.0`
353+
* - `NaN` is considered greater than all other values, but equal to itself
354+
*/
355+
def compare(that: Double): Int = java.lang.Double.compare(self, that)
356+
}
357+
}

0 commit comments

Comments
(0)

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