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
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 type `Person`.
41
42
42
-
typePerson=Record { valname:String; valage:Int }
43
-
```
43
+
This allows us to check at compiletime if accesses are valid:
44
44
45
-
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
46
-
program would print "Emma is 42 years old.":
45
+
```scala
46
+
valperson:Person=???
47
+
println(s"${person.name} is ${person.age} years old.") // works
48
+
println(person.email) // error: value email is not a member of Person
49
+
```
50
+
How is `Record` defined, and how does `person.name` resolve ?
51
+
52
+
`Record` is a class that extends the marker trait [`scala.Selectable`](https://scala-lang.org/api/3.x/scala/Selectable.html) and defines
53
+
a method `selectDynamic`, which maps a field name to its value.
54
+
Selecting a member of a structural type is syntactic sugar for a call to this method.
55
+
The selections `person.name` and `person.age` are translated by
The parent type `Record` in this example is a generic class that can represent arbitrary records in its `elems` argument. This argument is a
@@ -59,52 +81,45 @@ help from the user. In practice, the connection between a structural type
59
81
and its underlying generic representation would most likely be done by
60
82
a database layer, and therefore would not be a concern of the end user.
61
83
62
-
`Record` extends the marker trait [`scala.Selectable`](https://scala-lang.org/api/3.x/scala/Selectable.html) and defines
63
-
a method `selectDynamic`, which maps a field name to its value.
64
-
Selecting a structural type member is done by calling this method.
65
-
The `person.name` and `person.age` selections are translated by
66
-
the Scala compiler to:
67
-
68
-
```scala
69
-
person.selectDynamic("name").asInstanceOf[String]
70
-
person.selectDynamic("age").asInstanceOf[Int]
71
-
```
72
-
73
84
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
74
85
75
86
```scala
76
-
a.applyDynamic("f")(b, c)
87
+
a.applyDynamic("f")(b, c)
77
88
```
78
89
79
90
## Using Java Reflection
80
91
81
-
Structural types can also be accessed using [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html). Example:
92
+
Using `Selectable` and [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html), we can select a member from unrelated classes.
93
+
94
+
> Before resorting to structural calls with Java reflection one should consider alternatives. For instance, sometimes a more a modular _and_ efficient architecture can be obtained using [type classes](../contextual/type-classes.md).
95
+
96
+
For example, we would like to provide behavior for both [`FileInputStream`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/FileInputStream.html#%3Cinit%3E(java.io.File)) and [`Channel`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Channel.html) classes by calling their `close` method, however, these classes are unrelated, i.e. have no common supertype with a `close` method. Therefore, below we define a structural type `Closeable` that defines a `close` method.
82
97
83
98
```scala
84
-
typeCloseable= { defclose():Unit }
99
+
typeCloseable= { defclose():Unit }
85
100
86
-
classFileInputStream:
87
-
defclose():Unit
101
+
classFileInputStream:
102
+
defclose():Unit
88
103
89
-
classChannel:
90
-
defclose():Unit
104
+
classChannel:
105
+
defclose():Unit
91
106
```
92
107
93
-
Here, we define a structural type `Closeable` that defines a `close` method. There are various classes that have `close` methods, we just list [`FileInputStream`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/FileInputStream.html#%3Cinit%3E(java.io.File)) and [`Channel`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Channel.html) 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
94
-
all classes with a `close` method by using the `Closeable` type. For instance,
108
+
Ideally we would add a common interface to both these classes to define the `close` method, however they are defined in libraries outside of our control. As a compromise we can use the structural type to define a single implementation for an `autoClose` method:
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
104
-
of `reflectiveSelectable` shown above. What happens "under the hood" is then the following:
119
+
The call `f.close()`requires `Closeable`to extend `Selectable`to identify and call the `close` method in the receiver `f`. A universal implicit conversion to `Selectable` is enabled by an import
120
+
of `reflectiveSelectable` shown above, based on [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html). What happens "under the hood" is then the following:
105
121
106
-
- The import makes available an implicit conversion that turns any type into a
107
-
`Selectable`. `f` is wrapped in this conversion.
122
+
- The implicit conversion wraps `f` in an instance of `scala.reflect.Selectable` (which is a subtype of `Selectable`).
108
123
109
124
- The compiler then transforms the `close` call on the wrapped `f`
110
125
to an `applyDynamic` call. The end result is:
@@ -113,16 +128,14 @@ of `reflectiveSelectable` shown above. What happens "under the hood" is then the
113
128
reflectiveSelectable(f).applyDynamic("close")()
114
129
```
115
130
- The implementation of `applyDynamic` in `reflectiveSelectable`'s result
116
-
uses Java reflection to find and call a method `close` with zero parameters in the value referenced by `f` at runtime.
131
+
uses [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html) to find and call a method `close` with zero parameters in the value referenced by `f` at runtime.
117
132
118
133
Structural calls like this tend to be much slower than normal method calls. The mandatory import of `reflectiveSelectable` serves as a signpost that something inefficient is going on.
119
134
120
135
**Note:** In Scala 2, Java reflection is the only mechanism available for structural types and it is automatically enabled without needing the
121
136
`reflectiveSelectable` conversion. However, to warn against inefficient
122
137
dispatch, Scala 2 requires a language import `import scala.language.reflectiveCalls`.
123
138
124
-
Before resorting to structural calls with Java reflection one should consider alternatives. For instance, sometimes a more modular _and_ efficient architecture can be obtained using type classes.
125
-
126
139
## Extensibility
127
140
128
141
New instances of `Selectable` can be defined to support means of
@@ -179,13 +192,10 @@ differences.
179
192
is, as long as the correspondence of the structural type with the
180
193
underlying value is as stated.
181
194
182
-
-[`Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) is just a marker trait, which gives more leeway where and
183
-
how to define reflective access operations. By contrast
184
-
`Selectable` is a trait which declares the access operations.
185
-
186
195
- Two access operations, `selectDynamic` and `applyDynamic` are shared
187
196
between both approaches. In `Selectable`, `applyDynamic` also may also take
188
197
[`java.lang.Class`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html) arguments indicating the method's formal parameter types.
189
-
[`Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) comes with `updateDynamic`.
198
+
199
+
-`updateDynamic` is unique to [`Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) but as mentionned before, this fact is subject to change, and shouldn't be used as an assumption.
0 commit comments