Make genericNumberLiterals non experimental?

Is there a plan to make genericNumberLiterals non experimental? 3.7.0? I’ve been using it in several projects and found it quite convenient, except for that hassle of being experimental.

2 Likes

The PR for the SIP is still pending.

It does not work well with extensions :frowning:

import scala.util.FromDigits
import scala.math.BigDecimal
import scala.language.experimental.genericNumberLiterals

object Test{

  extension (value: BigDecimal){
    def extToStr(): String = value.toString()
  } 
  def toStr(value: BigDecimal): String = value.toString()

  @main def run():Unit = {
    println( toStr(123456789012345678901234567890.1))   // 123456789012345678901234567890.1
    println(123456789012345678901234567890.1.extToStr()) // 1.2345678901234568E+29
  }  
}

When we do import ..genericNumberLiterals, toStr works correctly but extToStr continue to loose percision.

It is very confusing.

I have the same issue in my computer algebra program. I decided to use a constructor to force the target type - to BigInt, in my case. In yours, that would be:

BigDecimal(123456789012345678901234567890.1).extToStr()

, which looks slightly better than:

BigDecimal("123456789012345678901234567890.1")

extension methods (when called after ‘.’) can not introduce a target type, they work on whatever the direct type of the value is.

I don’t remember seeing that anywhere, is it documented ?

IMHO:
In such cases it leads to error prone code because of compiler which does not check this errors at all.
It is more safe just to always use string ("3.14".extNum) instead of waiting errors which will be come regulary when programmers write code by reflex.

IMO,
If we mainly require literals for BigInt/BigDecimal,
then there is no need for the Generic Number Literals feature. Simply support the number tokens to end with # to get either:

val bigOne: BigInt = 1#
val bigDecimalOne: BigDecimal = 1.0#
val bigBinary: BigInt = 0b0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010#
val bigHex: BigInt = 
0x12345454365465765aba534bade873546548343535#

Instead of # we can think of other endings. We need a single character that won’t look like a unit of measurement or size (e.g., W looks like Watt, G would look like Giga), and it cannot be B for hexadecimal compatibility.

And if we really require generic literals,
then the current feature is not generic enough and is very limited in its application since it cannot interact well with extension methods and implicit conversions. String interpolation should be enough for such cases, but, if it’s still too verbose, then consider the sharp interpolation feature, instead.

So in summary, the currently generic number literals feature, IMO, is a half-measure that over-complicates the common cases and doesn’t handle the real generic ones. It should be dropped from the language.

I’ve never used them with anything other than BigInt so I could live with number literals being restricted to a few fixed types. However, I’ll never use something like the # sign. If number literals stop looking like number literals, what’s the point? I’ll use strings instead and map a conversion to numbers somewhere.

3 Likes

Scala already has L suffix for Long literal indications, so I really don’t understand your reluctance.

Not sure. Maybe because I don’t use L that much?

My most recent use of big literals was in test data:

private val set1 = Set[BigInt](-1391825088924, -13780446425, -13780446223, -12208992021, -12208991793, -12208980381, -133801043, -133790844, -133790642, -133790638, ...)

I can’t see myself adding a # after each number. Actually, I’d like to be able to write this too:

private val set1 = Set[Long](-1391825088924, -13780446425, -13780446223, -12208992021, -12208991793, -12208980381, -133801043, -133790844, -133790642, -133790638, ...)

without any L (which contradicts my previous statement that I only care about BigInt).