You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
for other <- others do bldr.append(" and ").append(other)
22
23
bldr.toString
23
24
```
25
+
24
26
This would generate a main program `happyBirthday` that could be called like this
27
+
25
28
```
26
29
> scala happyBirthday 23 Lisa Peter
27
30
Happy 23rd Birthday, Lisa and Peter!
28
31
```
32
+
29
33
A `@main` annotated method can be written either at the top-level or in a statically accessible object. The name of the program is in each case the name of the method, without any object prefixes. The `@main` method can have an arbitrary number of parameters.
30
34
For each parameter type there must be an instance of the `scala.util.FromString` type class
31
35
that is used to convert an argument string to the required parameter type.
@@ -52,9 +56,10 @@ The Scala compiler generates a program from a `@main` method `f` as follows:
52
56
- The class has a static method `main` with the usual signature. It takes an `Array[String]`
53
57
as argument and returns `Unit`.
54
58
- The generated `main` method calls method `f` with arguments converted using
55
-
methods in the `scala.util.CommandLineParser` object.
59
+
methods in the [`scala.util.CommandLineParser` object](https://dotty.epfl.ch/api/scala/util/CommandLineParser$.html).
56
60
57
61
For instance, the `happyBirthDay` method above would generate additional code equivalent to the following class:
62
+
58
63
```scala
59
64
finalclasshappyBirthday:
60
65
importscala.util.{CommandLineParser=>CLP}
@@ -67,6 +72,7 @@ final class happyBirthday:
67
72
catch
68
73
caseerror: CLP.ParseError=>CLP.showError(error)
69
74
```
75
+
70
76
**Note**: The `<static>` modifier above expresses that the `main` method is generated
71
77
as a static method of class `happyBirthDay`. It is not available for user programs in Scala. Regular "static" members are generated in Scala using objects instead.
The previous functionality of `App`, which relied on the "magic" `DelayedInit` trait, is no longer available. `App` still exists in limited form for now, but it does not support command line arguments and will be deprecated in the future. If programs need to cross-build
87
+
The previous functionality of `App`, which relied on the "magic" [`DelayedInit`](../dropped-features/delayed-init.md) trait, is no longer available. `App` still exists in limited form for now, but it does not support command line arguments and will be deprecated in the future. If programs need to cross-build
82
88
between Scala 2 and Scala 3, it is recommended to use an explicit `main` method with an `Array[String]` argument instead.
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:
12
+
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:
13
+
12
14
```scala
13
15
valx:Long=-10_000_000_000
14
16
valy:BigInt=0x123_abc_789_def_345_678_901
@@ -17,6 +19,7 @@ val z: BigDecimal = 110_222_799_799.99
17
19
(y: BigInt) match
18
20
case123_456_789_012_345_678_901=>
19
21
```
22
+
20
23
The syntax of numeric literals is the same as before, except there are no pre-set limits
Copy file name to clipboardExpand all lines: docs/docs/reference/changed-features/structural-types-spec.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,9 +30,11 @@ the methods `selectDynamic` and `applyDynamic`. The methods could be members of
30
30
31
31
The `selectDynamic` method takes a field name and returns the value associated with that name in the `Selectable`.
32
32
It should have a signature of the form:
33
+
33
34
```scala
34
35
defselectDynamic(name: String):T
35
36
```
37
+
36
38
Often, the return type `T` is `Any`.
37
39
38
40
Unlike `scala.Dynamic`, there is no special meaning for an `updateDynamic` method.
@@ -41,10 +43,12 @@ Consequently, it is recommended not to define any member called `updateDynamic`
41
43
42
44
The `applyDynamic` method is used for selections that are applied to arguments. It takes a method name and possibly `Class`es representing its parameters types as well as the arguments to pass to the function.
43
45
Its signature should be of one of the two following forms:
Both versions are passed the actual arguments in the `args` parameter. The second version takes in addition a vararg argument of `java.lang.Class`es that identify the method's parameter classes. Such an argument is needed
49
53
if `applyDynamic` is implemented using Java reflection, but it could be
50
54
useful in other cases as well. `selectDynamic` and `applyDynamic` can also take additional context parameters in using clauses. These are resolved in the normal way at the callsite.
@@ -94,5 +98,4 @@ conversion that can turn `v` into a `Selectable`, and the selection methods coul
The type `Person` adds a _refinement_ to its parent type `Record` that defines the two fields `name` and `age`. We say the refinement is _structural_ since `name` and `age` are not defined in the parent type. But they exist nevertheless as members of class `Person`. For instance, the following
println(s"${person.name} is ${person.age} years old.")
47
50
```
51
+
48
52
The parent type `Record` in this example is a generic class that can represent arbitrary records in its `elems` argument. This argument is a
49
53
sequence of pairs of labels of type `String` and values of type `Any`.
50
54
When we create a `Person` as a `Record` we have to assert with a typecast
@@ -59,19 +63,22 @@ a method `selectDynamic`, which maps a field name to its value.
59
63
Selecting a structural type member is done by calling this method.
60
64
The `person.name` and `person.age` selections are translated by
61
65
the Scala compiler to:
66
+
62
67
```scala
63
68
person.selectDynamic("name").asInstanceOf[String]
64
69
person.selectDynamic("age").asInstanceOf[Int]
65
70
```
66
71
67
72
Besides `selectDynamic`, a `Selectable` class sometimes also defines a method `applyDynamic`. This can then be used to translate function calls of structural members. So, if `a` is an instance of `Selectable`, a structural call like `a.f(b, c)` would translate to
73
+
68
74
```scala
69
75
a.applyDynamic("f")(b, c)
70
76
```
71
77
72
78
## Using Java Reflection
73
79
74
80
Structural types can also be accessed using [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html). Example:
81
+
75
82
```scala
76
83
typeCloseable= { defclose():Unit }
77
84
@@ -81,14 +88,17 @@ Structural types can also be accessed using [Java reflection](https://www.oracle
81
88
classChannel:
82
89
defclose():Unit
83
90
```
91
+
84
92
Here, we define a structural type `Closeable` that defines a `close` method. There are various classes that have `close` methods, we just list `FileInputStream` and `Channel` as two examples. It would be easiest if the two classes shared a common interface that factors out the `close` method. But such factorings are often not possible if different libraries are combined in one application. Yet, we can still have methods that work on
85
93
all classes with a `close` method by using the `Closeable` type. For instance,
The call `f.close()` has to use Java reflection to identify and call the `close` method in the receiver `f`. This needs to be enabled by an import
93
103
of `reflectiveSelectable` shown above. What happens "under the hood" is then the following:
94
104
@@ -122,6 +132,7 @@ the database access example given at the beginning of this document.
122
132
123
133
Local and anonymous classes that extend `Selectable` get more refined types
124
134
than other classes. Here is an example:
135
+
125
136
```scala
126
137
traitVehicleextends reflect.Selectable:
127
138
valwheels:Int
@@ -132,12 +143,14 @@ val i3 = new Vehicle: // i3: Vehicle { val range: Int }
132
143
133
144
i3.range
134
145
```
146
+
135
147
The type of `i3` in this example is `Vehicle { val range: Int }`. Hence,
136
148
`i3.range` is well-formed. Since the base class `Vehicle` does not define a `range` field or method, we need structural dispatch to access the `range` field of the anonymous class that initializes `id3`. Structural dispatch
137
149
is implemented by the base trait `reflect.Selectable` of `Vehicle`, which
138
150
defines the necessary `selectDynamic` member.
139
151
140
152
`Vehicle` could also extend some other subclass of `scala.Selectable` that implements `selectDynamic` and `applyDynamic` differently. But if it does not extend a `Selectable` at all, the code would no longer typecheck:
153
+
141
154
```scala
142
155
traitVehicle:
143
156
valwheels:Int
@@ -148,6 +161,7 @@ val i3 = new Vehicle: // i3: Vehicle
148
161
149
162
i3.range // error: range is not a member of `Vehicle`
150
163
```
164
+
151
165
The difference is that the type of an anonymous class that does not extend `Selectable` is just formed from the parent type(s) of the class, without
152
166
adding any refinements. Hence, `i3` now has just type `Vehicle` and the selection `i3.range` gives a "member not found" error.
0 commit comments