Skip to content

Favour error over implicit conversions with generic number literals #7468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update numeric literals documentation
  • Loading branch information
bishabosha committed Nov 7, 2019
commit 0a720d8d7009369c1dc7b10407cb1f1ad07b0ac6
34 changes: 32 additions & 2 deletions docs/docs/reference/changed-features/numeric-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ val y: BigInt = 0x123_abc_789_def_345_678_901
val z: BigDecimal = 111222333444.55
```
are legal by rule (2), since both `BigInt` and `BigDecimal` have `FromDigits` instances
(which implement the `FromDigits` subclasses `FromDigits.WithRadix` and `FromDigits.Decimal`, respectively).
(which implement the `FromDigits` subclasses `FromDigits.WithRadix` and `FromDigits.Floating`, respectively).
On the other hand,
```scala
val x = -10_000_000_000
Expand Down Expand Up @@ -112,6 +112,36 @@ class NumberTooSmall (msg: String = "number too small") extends FromDigi
class MalformedNumber(msg: String = "malformed number literal") extends FromDigitsException(msg)
```

### Compiler Expansion

The companion object `FromDigits` also defines four methods that may be used to provide a given instance of one of the subclasses of `FromDigits`:
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this is not enough. For instance, it is said that the companion object defines a method fromDecimalDigits but it is not specified where and when this method is called.

I believe if we want to this to go ahead, it needs a complete rework of the doc page, and the SIP committee has to decide on the changes. My concern is that once the additions are fully worked in, we might decide that things became too complex.

```scala
inline def fromDigits[T](given x: FromDigits[T]): x.type = x

inline def fromRadixDigits[T](given x: FromDigits.WithRadix[T]): x.type = x

inline def fromDecimalDigits[T](given x: FromDigits.Decimal[T]): x.type = x

inline def fromFloatingDigits[T](given x: FromDigits.Floating[T]): x.type = x
```

If a numeric literal has a known expected type `T` that is not one of the primitive numeric types, then the compiler will search for a given instance of `FromDigits[T]`. If one exists, then the compiler expands the literal to a call on the `fromDigits` method on the result obtained from calling one of the above four methods.

As an example, the literal below has a nonsensical expected type `BigDecimal`, which can not be constructed with hex digits:
```scala
0xCAFEBABE: BigDecimal
```
Upon the compiler finding a given instance for `FromDigits[BigDecimal]`, the hex literal above expands to the following:
```scala
FromDigits.fromRadixDigits[BigDecimal].fromDigits("0CAFEBABE", 16)
```
The given clause of `fromRadixDigits` asserts that the prior found `FromDigits` instance is a subtype of `FromDigits.WithRadix[BigDecimal]`, or else following error is issued:
```scala
1 |0xCAFEBABE: BigDecimal
|^
|Type BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10.
```

### Example

As a fully worked out example, here is an implementation of a new numeric class, `BigFloat`, that accepts numeric literals. `BigFloat` is defined in terms of a `BigInt` mantissa and an `Int` exponent:
Expand Down Expand Up @@ -174,7 +204,7 @@ With the setup of the previous section, a literal like
```
would be expanded by the compiler to
```scala
BigFloat.FromDigits.fromDigits("1e100000000000")
FromDigits.fromFloatingDigits[BigFloat].fromDigits("1e100000000000")
```
Evaluating this expression throws a `NumberTooLarge` exception at run time. We would like it to
produce a compile-time error instead. We can achieve this by tweaking the `BigFloat` class
Expand Down