Skip to content

Commit 3fb1b6d

Browse files
committed
Organize experimental language features documentaion
1 parent 7ed0907 commit 3fb1b6d

File tree

7 files changed

+301
-285
lines changed

7 files changed

+301
-285
lines changed

docs/docs/reference/changed-features/numeric-literals.md

Lines changed: 1 addition & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -4,259 +4,4 @@ title: "Numeric Literals"
44
movedTo: https://docs.scala-lang.org/scala3/reference/changed-features/numeric-literals.html
55
---
66

7-
**Note**: This feature is not yet part of the Scala 3 language definition. It can be made available by a language import:
8-
9-
```scala
10-
import scala.language.experimental.genericNumberLiterals
11-
```
12-
13-
In Scala 2, numeric literals were confined to the primitive numeric types `Int`, `Long`, `Float`, and `Double`. Scala 3 allows to write numeric literals also for user-defined types. Example:
14-
15-
```scala
16-
val x: Long = -10_000_000_000
17-
val y: BigInt = 0x123_abc_789_def_345_678_901
18-
val z: BigDecimal = 110_222_799_799.99
19-
20-
(y: BigInt) match
21-
case 123_456_789_012_345_678_901 =>
22-
```
23-
24-
The syntax of numeric literals is the same as before, except there are no pre-set limits
25-
how large they can be.
26-
27-
### Meaning of Numeric Literals
28-
29-
The meaning of a numeric literal is determined as follows:
30-
31-
- If the literal ends with `l` or `L`, it is a `Long` integer (and must fit in its legal range).
32-
- If the literal ends with `f` or `F`, it is a single precision floating point number of type `Float`.
33-
- If the literal ends with `d` or `D`, it is a double precision floating point number of type `Double`.
34-
35-
In each of these cases the conversion to a number is exactly as in Scala 2 or in Java. If a numeric literal does _not_ end in one of these suffixes, its meaning is determined by the expected type:
36-
37-
1. If the expected type is `Int`, `Long`, `Float`, or `Double`, the literal is
38-
treated as a standard literal of that type.
39-
2. If the expected type is a fully defined type `T` that has a given instance of type
40-
`scala.util.FromDigits[T]`, the literal is converted to a value of type `T` by passing it as an argument to
41-
the `fromDigits` method of that instance (more details below).
42-
3. Otherwise, the literal is treated as a `Double` literal (if it has a decimal point or an
43-
exponent), or as an `Int` literal (if not). (This last possibility is again as in Scala 2 or Java.)
44-
45-
With these rules, the definition
46-
47-
```scala
48-
val x: Long = -10_000_000_000
49-
```
50-
51-
is legal by rule (1), since the expected type is `Long`. The definitions
52-
53-
```scala
54-
val y: BigInt = 0x123_abc_789_def_345_678_901
55-
val z: BigDecimal = 111222333444.55
56-
```
57-
58-
are legal by rule (2), since both `BigInt` and `BigDecimal` have `FromDigits` instances
59-
(which implement the `FromDigits` subclasses `FromDigits.WithRadix` and `FromDigits.Decimal`, respectively).
60-
On the other hand,
61-
62-
```scala
63-
val x = -10_000_000_000
64-
```
65-
66-
gives a type error, since without an expected type `-10_000_000_000` is treated by rule (3) as an `Int` literal, but it is too large for that type.
67-
68-
### The FromDigits Trait
69-
70-
To allow numeric literals, a type simply has to define a `given` instance of the
71-
`scala.util.FromDigits` type class, or one of its subclasses. `FromDigits` is defined
72-
as follows:
73-
74-
```scala
75-
trait FromDigits[T]:
76-
def fromDigits(digits: String): T
77-
```
78-
79-
Implementations of the `fromDigits` convert strings of digits to the values of the
80-
implementation type `T`.
81-
The `digits` string consists of digits between `0` and `9`, possibly preceded by a
82-
sign ("+" or "-"). Number separator characters `_` are filtered out before
83-
the string is passed to `fromDigits`.
84-
85-
The companion object `FromDigits` also defines subclasses of `FromDigits` for
86-
whole numbers with a given radix, for numbers with a decimal point, and for
87-
numbers that can have both a decimal point and an exponent:
88-
89-
```scala
90-
object FromDigits:
91-
92-
/** A subclass of `FromDigits` that also allows to convert whole
93-
* number literals with a radix other than 10
94-
*/
95-
trait WithRadix[T] extends FromDigits[T]:
96-
def fromDigits(digits: String): T = fromDigits(digits, 10)
97-
def fromDigits(digits: String, radix: Int): T
98-
99-
/** A subclass of `FromDigits` that also allows to convert number
100-
* literals containing a decimal point ".".
101-
*/
102-
trait Decimal[T] extends FromDigits[T]
103-
104-
/** A subclass of `FromDigits`that allows also to convert number
105-
* literals containing a decimal point "." or an
106-
* exponent `('e' | 'E')['+' | '-']digit digit*`.
107-
*/
108-
trait Floating[T] extends Decimal[T]
109-
```
110-
111-
A user-defined number type can implement one of those, which signals to the compiler
112-
that hexadecimal numbers, decimal points, or exponents are also accepted in literals
113-
for this type.
114-
115-
### Error Handling
116-
117-
`FromDigits` implementations can signal errors by throwing exceptions of some subtype
118-
of `FromDigitsException`. `FromDigitsException` is defined with three subclasses in the
119-
`FromDigits` object as follows:
120-
121-
```scala
122-
abstract class FromDigitsException(msg: String) extends NumberFormatException(msg)
123-
124-
class NumberTooLarge (msg: String = "number too large") extends FromDigitsException(msg)
125-
class NumberTooSmall (msg: String = "number too small") extends FromDigitsException(msg)
126-
class MalformedNumber(msg: String = "malformed number literal") extends FromDigitsException(msg)
127-
```
128-
129-
### Example
130-
131-
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:
132-
133-
```scala
134-
case class BigFloat(mantissa: BigInt, exponent: Int):
135-
override def toString = s"${mantissa}e${exponent}"
136-
```
137-
138-
`BigFloat` literals can have a decimal point as well as an exponent. E.g. the following expression
139-
should produce the `BigFloat` number `BigFloat(-123, 997)`:
140-
141-
```scala
142-
-0.123E+1000: BigFloat
143-
```
144-
145-
The companion object of `BigFloat` defines an `apply` constructor method to construct a `BigFloat`
146-
from a `digits` string. Here is a possible implementation:
147-
148-
```scala
149-
object BigFloat:
150-
import scala.util.FromDigits
151-
152-
def apply(digits: String): BigFloat =
153-
val (mantissaDigits, givenExponent) =
154-
digits.toUpperCase.split('E') match
155-
case Array(mantissaDigits, edigits) =>
156-
val expo =
157-
try FromDigits.intFromDigits(edigits)
158-
catch case ex: FromDigits.NumberTooLarge =>
159-
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
160-
(mantissaDigits, expo)
161-
case Array(mantissaDigits) =>
162-
(mantissaDigits, 0)
163-
val (intPart, exponent) =
164-
mantissaDigits.split('.') match
165-
case Array(intPart, decimalPart) =>
166-
(intPart ++ decimalPart, givenExponent - decimalPart.length)
167-
case Array(intPart) =>
168-
(intPart, givenExponent)
169-
BigFloat(BigInt(intPart), exponent)
170-
```
171-
172-
To accept `BigFloat` literals, all that's needed in addition is a `given` instance of type
173-
`FromDigits.Floating[BigFloat]`:
174-
175-
```scala
176-
given FromDigits: FromDigits.Floating[BigFloat] with
177-
def fromDigits(digits: String) = apply(digits)
178-
end BigFloat
179-
```
180-
181-
Note that the `apply` method does not check the format of the `digits` argument. It is
182-
assumed that only valid arguments are passed. For calls coming from the compiler
183-
that assumption is valid, since the compiler will first check whether a numeric
184-
literal has the correct format before it gets passed on to a conversion method.
185-
186-
### Compile-Time Errors
187-
188-
With the setup of the previous section, a literal like
189-
190-
```scala
191-
1e10_0000_000_000: BigFloat
192-
```
193-
194-
would be expanded by the compiler to
195-
196-
```scala
197-
BigFloat.FromDigits.fromDigits("1e100000000000")
198-
```
199-
200-
Evaluating this expression throws a `NumberTooLarge` exception at run time. We would like it to
201-
produce a compile-time error instead. We can achieve this by tweaking the `BigFloat` class
202-
with a small dose of metaprogramming. The idea is to turn the `fromDigits` method
203-
into a macro, i.e. make it an inline method with a splice as right-hand side.
204-
To do this, replace the `FromDigits` instance in the `BigFloat` object by the following two definitions:
205-
206-
```scala
207-
object BigFloat:
208-
...
209-
210-
class FromDigits extends FromDigits.Floating[BigFloat]:
211-
def fromDigits(digits: String) = apply(digits)
212-
213-
given FromDigits with
214-
override inline def fromDigits(digits: String) = ${
215-
fromDigitsImpl('digits)
216-
}
217-
```
218-
219-
Note that an inline method cannot directly fill in for an abstract method, since it produces
220-
no code that can be executed at runtime. That is why we define an intermediary class
221-
`FromDigits` that contains a fallback implementation which is then overridden by the inline
222-
method in the `FromDigits` given instance. That method is defined in terms of a macro
223-
implementation method `fromDigitsImpl`. Here is its definition:
224-
225-
```scala
226-
private def fromDigitsImpl(digits: Expr[String])(using ctx: Quotes): Expr[BigFloat] =
227-
digits.value match
228-
case Some(ds) =>
229-
try
230-
val BigFloat(m, e) = apply(ds)
231-
'{BigFloat(${Expr(m)}, ${Expr(e)})}
232-
catch case ex: FromDigits.FromDigitsException =>
233-
ctx.error(ex.getMessage)
234-
'{BigFloat(0, 0)}
235-
case None =>
236-
'{apply($digits)}
237-
end BigFloat
238-
```
239-
240-
The macro implementation takes an argument of type `Expr[String]` and yields
241-
a result of type `Expr[BigFloat]`. It tests whether its argument is a constant
242-
string. If that is the case, it converts the string using the `apply` method
243-
and lifts the resulting `BigFloat` back to `Expr` level. For non-constant
244-
strings `fromDigitsImpl(digits)` is simply `apply(digits)`, i.e. everything is
245-
evaluated at runtime in this case.
246-
247-
The interesting part is the `catch` part of the case where `digits` is constant.
248-
If the `apply` method throws a `FromDigitsException`, the exception's message is issued as a compile time error in the `ctx.error(ex.getMessage)` call.
249-
250-
With this new implementation, a definition like
251-
252-
```scala
253-
val x: BigFloat = 1234.45e3333333333
254-
```
255-
256-
would give a compile time error message:
257-
258-
```scala
259-
3 | val x: BigFloat = 1234.45e3333333333
260-
| ^^^^^^^^^^^^^^^^^^
261-
| exponent too large: 3333333333
262-
```
7+
[Document was moved](../experimental/numeric-literals.md)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
layout: doc-page
3+
title: "Named Type Arguments"
4+
---
5+
6+
**Note:** This feature is implemented in Scala 3, but is not expected to be part of Scala 3.0.
7+
8+
Type arguments of methods can now be specified by name as well as by position. Example:
9+
10+
``` scala
11+
def construct[Elem, Coll[_]](xs: Elem*): Coll[Elem] = ???
12+
13+
val xs1 = construct[Coll = List, Elem = Int](1, 2, 3)
14+
val xs2 = construct[Coll = List](1, 2, 3)
15+
```
16+
17+
Similar to a named value argument `(x = e)`, a named type argument
18+
`[X = T]` instantiates the type parameter `X` to the type `T`.
19+
Named type arguments do not have to be in order (see `xs1` above) and
20+
unspecified arguments are inferred by the compiler (see `xs2` above).
21+
Type arguments must be all named or un-named, mixtures of named and
22+
positional type arguments are not supported.
23+
24+
## Motivation
25+
26+
The main benefit of named type arguments is that unlike positional arguments,
27+
you are allowed to omit passing arguments for some parameters, like in the
28+
definition of `xs2` above. A missing type argument is inferred as usual by
29+
local type inference. This is particularly useful in situations where some type
30+
arguments can be easily inferred from others.
31+
32+
[More details](./named-typeargs-spec.md)

0 commit comments

Comments
 (0)