Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

sjrd
Copy link
Member

@sjrd sjrd commented Sep 3, 2025

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.


This would be a required step before we do anything about #23824.

…hods.

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.
@hamzaremmal
Copy link
Member

@sjrd you have beaten me to it 😄

@soronpo
Copy link
Contributor

soronpo commented Sep 4, 2025

The problem is that extension methods are not equivalent to implicit classes/definition extensions. Without solving https://contributors.scala-lang.org/t/relaxed-extension-methods-sip-54-are-not-relaxed-enough/6585/1 this change will break everywhere that has methods in the same name in scope.

@sjrd
Copy link
Member Author

sjrd commented Sep 4, 2025

The problem is that extension methods are not equivalent to implicit classes/definition extensions. Without solving https://contributors.scala-lang.org/t/relaxed-extension-methods-sip-54-are-not-relaxed-enough/6585/1 this change will break everywhere that has methods in the same name in scope.

I don't see how that problem appears when defining extensions for monomorphic types. You wouldn't redefine another extension of toHexString for Int (or if you do, it's on you), so you won't have a clash like that. With polymorphic types there are reasons to define separate extensions for Foo[Int] and Foo[String], but for a monomorphic type that's not the case.

Comment on lines +495 to +496
@deprecated("isWhole on Byte is always true", "2.12.15")
def isWhole: Boolean = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why this was done, but it feels weird to say that an extension method was deprecated since 2.12.15 😅

@soronpo
Copy link
Contributor

soronpo commented Sep 4, 2025

The problem is that extension methods are not equivalent to implicit classes/definition extensions. Without solving https://contributors.scala-lang.org/t/relaxed-extension-methods-sip-54-are-not-relaxed-enough/6585/1 this change will break everywhere that has methods in the same name in scope.

I don't see how that problem appears when defining extensions for monomorphic types. You wouldn't redefine another extension of toHexString for Int (or if you do, it's on you), so you won't have a clash like that. With polymorphic types there are reasons to define separate extensions for Foo[Int] and Foo[String], but for a monomorphic type that's not the case.

You are right. I thought the implementation used a typeclass approach + extension methods. My mistake.

*
* @param end The final bound of the range to make.
*/
def until(end: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is part of the very little subset where everyone agrees on it :). Same goes for to.

Suggested change
def until(end: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, 1)
infix def until(end: Byte): NumericRange.Exclusive[Byte] = NumericRange(self, end, 1)

Comment on lines +525 to +528
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(my personal preferences here)

Suggested change
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
infix def max(that: Byte): Byte = java.lang.Math.max(self.toInt, that.toInt).toByte
/** Returns `this` if `this < that` or `that` otherwise. */
infix def min(that: Byte): Byte = java.lang.Math.min(self.toInt, that.toInt).toByte

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants