@@ -3,131 +3,77 @@ layout: doc-page
3
3
title : " Programmatic Structural Types"
4
4
---
5
5
6
- Previously, Scala supported structural types by means of
7
- reflection. This is problematic on other platforms, because Scala's
8
- reflection is JVM-based. Consequently, Scala.js and Scala.native don't
9
- support structural types fully. The reflection based implementation is
10
- also needlessly restrictive, since it rules out other implementation
11
- schemes. This makes structural types unsuitable for e.g. modelling
12
- rows in a database, for which they would otherwise seem to be an ideal
13
- match.
14
-
15
- Dotty allows to implement structural types programmatically, using
16
- "Selectables". ` Selectable ` is a trait defined as follows:
17
-
18
- trait Selectable extends Any {
19
- def selectDynamic(name: String): Any
20
- def selectDynamicMethod(name: String, paramClasses: ClassTag[_]*): Any =
21
- new UnsupportedOperationException("selectDynamicMethod")
22
- }
23
-
24
- The most important method of a ` Selectable ` is ` selectDynamic ` : It
25
- takes a field name and returns the value associated with that name in
26
- the selectable.
27
-
28
- Assume now ` r ` is a value with structural type ` S ` . In general ` S ` is
29
- of the form ` C { Rs } ` , i.e. it consists of a class reference ` C ` and
30
- refinement declarations ` Rs ` . We call a field selection ` r.f `
31
- _ structural_ if ` f ` is a name defined by a declaration in ` Rs ` whereas
32
- ` C ` defines no member of name ` f ` . Assuming the selection has type
33
- ` T ` , it is mapped to something equivalent to the following code:
34
-
35
- (r: Selectable).selectDynamic("f").asInstanceOf[T]
36
-
37
- That is, we make sure ` r ` conforms to type ` Selectable ` , potentially
38
- by adding an implicit conversion. We then invoke the ` get ` operation
39
- of that instance, passing the the name ` "f" ` as a parameter. We
40
- finally cast the resulting value back to the statically known type
41
- ` T ` .
42
-
43
- ` Selectable ` also defines another access method called
44
- ` selectDynamicMethod ` . This operation is used to select methods
45
- instead of fields. It gets passed the class tags of the selected
46
- method's formal parameter types as additional arguments. These can
47
- then be used to disambiguate one of several overloaded variants.
48
-
49
- Package ` scala.reflect ` contains an implicit conversion which can map
50
- any value to a selectable that emulates reflection-based selection, in
51
- a way similar to what was done until now:
52
-
53
- package scala.reflect
54
-
55
- object Selectable {
56
- implicit def reflectiveSelectable(receiver: Any): scala.Selectable =
57
- receiver match {
58
- case receiver: scala.Selectable => receiver
59
- case _ => new scala.reflect.Selectable(receiver)
60
- }
61
- }
62
-
63
- When imported, ` reflectiveSelectable ` provides a way to access fields
64
- of any structural type using Java reflection. This is similar to the
65
- current implementation of structural types. The main difference is
66
- that to get reflection-based structural access one now has to add an
67
- import:
68
-
69
- import scala.reflect.Selectable.reflectiveSelectable
70
-
71
- On the other hand, the previously required language feature import of
72
- ` reflectiveCalls ` is now redundant and is therefore dropped.
73
-
74
- As you can see from its implementation above, ` reflectSelectable `
75
- checks first whether its argument is already a run-time instance of
76
- ` Selectable ` , in which case it is returned directly. This means that
77
- reflection-based accesses only take place as a last resort, if no
78
- other ` Selectable ` is defined.
79
-
80
- Other selectable instances can be defined in libraries. For instance,
81
- here is a simple class of records that support dynamic selection:
82
-
83
- case class Record(elems: (String, Any)*) extends Selectable {
84
- def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2
85
- }
86
-
87
- ` Record ` consists of a list of pairs of element names and values. Its
88
- ` selectDynamic ` operation finds the pair with given name and returns
89
- its value.
90
-
91
- For illustration, let's define a record value and cast it to a
92
- structural type ` Person ` :
93
-
94
- type Person = Record { val name: String; val age: Int }
6
+ Some usecases, such as modelling database access, are more awkward in
7
+ statically typed languages than in dynamically typed languages: With
8
+ dynamically typed languages, it's quite natural to model a row as a
9
+ record or object, and to select entries with simple dot notation (e.g.
10
+ ` row.columnName ` ).
11
+
12
+ Achieving the same experience in statically typed
13
+ language requires defining a class for every possible row arising from
14
+ database manipulation (including rows arising from joins and
15
+ projections) and setting up a scheme to map between a row and the
16
+ class representing it.
17
+
18
+ This requires a large amount of boilerplate, which leads developers to
19
+ trade the advantages of static typing for simpler schemes where colum
20
+ names are represented as strings and passed to other operators (e.g.
21
+ ` row.select("columnName") ` ). This approach forgoes the advantages of
22
+ static typing, and is still not as natural as the dynamically typed
23
+ version.
24
+
25
+ Structural types help in situations where we would like to support
26
+ simple dot notation in dynamic contexts without losing the advantages
27
+ of static typing. They allow developers to use dot notation and
28
+ configure how fields and methods should be resolved.
29
+
30
+ ## Example
31
+
32
+ ``` scala
33
+ object StructuralTypeExample {
34
+
35
+ case class Record (elems : (String , Any )* ) extends Selectable {
36
+ def selectDynamic (name : String ): Any = elems.find(_._1 == name).get._2
37
+ }
38
+
39
+ type Person = Record {
40
+ val name : String
41
+ val age : Int
42
+ }
43
+
44
+ def main (args : Array [String ]): Unit = {
95
45
val person = Record (" name" -> " Emma" , " age" -> 42 ).asInstanceOf [Person ]
46
+ println(s " ${person.name} is ${person.age} years old. " )
47
+ // Prints: Emma is 42 years old.
48
+ }
49
+ }
50
+ ```
96
51
97
- Then ` person.name ` will have static type ` String ` , and will produce ` "Emma" ` as result.
52
+ ## Extensibility
98
53
99
- The safety of this scheme relies on the correctness of the cast. If
100
- the cast lies about the structure of the record, the corresponding
101
- ` selectDynamic ` operation would fail. In practice, the cast would
102
- likely be part if a database access layer which would ensure its
103
- correctness.
54
+ New instances of ` Selectable ` can be defined to support means of
55
+ access other than Java reflection, which would enable usages such as
56
+ the database access example given at the beginning of this document.
104
57
105
- ### Notes:
58
+ ## Relation with ` scala.Dynamic `
106
59
107
- 1 . The scheme does not handle polymorphic methods in structural
108
- refinements. Such polymorphic methods are currently flagged as
109
- errors. It's not clear whether the use case is common enough to
110
- warrant the additional complexity of supporting it.
111
-
112
- 2 . There are clearly some connections with ` scala.Dynamic ` here, since
60
+ There are clearly some connections with ` scala.Dynamic ` here, since
113
61
both select members programmatically. But there are also some
114
62
differences.
115
63
116
- - Fully dynamic selection is not typesafe, but structural selection
117
- is, as long as the correspondence of the structural type with the
118
- underlying value is as stated.
119
-
120
- - ` Dynamic ` is just a marker trait, which gives more leeway where and
121
- how to define reflective access operations. By contrast
122
- ` Selectable ` is a trait which declares the access operations.
64
+ - Fully dynamic selection is not typesafe, but structural selection
65
+ is, as long as the correspondence of the structural type with the
66
+ underlying value is as stated.
123
67
124
- - One access operation, ` selectDynamic ` is shared between both
125
- approaches, but the other access operations are
126
- different. ` Selectable ` defines a ` selectDynamicMethod ` , which
127
- takes class tags indicating the method's formal parameter types as
128
- additional argument. ` Dynamic ` comes with ` applyDynamic ` and
129
- ` updateDynamic ` methods, which take actual argument values.
68
+ - ` Dynamic ` is just a marker trait, which gives more leeway where and
69
+ how to define reflective access operations. By contrast
70
+ ` Selectable ` is a trait which declares the access operations.
130
71
131
- ### Reference
72
+ - One access operation, ` selectDynamic ` is shared between both
73
+ approaches, but the other access operations are
74
+ different. ` Selectable ` defines a ` selectDynamicMethod ` , which
75
+ takes class tags indicating the method's formal parameter types as
76
+ additional argument. ` Dynamic ` comes with ` applyDynamic ` and
77
+ ` updateDynamic ` methods, which take actual argument values.
132
78
133
- For more info, see [ Issue # 1886 ] ( https://github.com/lampepfl/dotty/issues/1886 ) .
79
+ [ More details ] ( structural-types-spec.html )
0 commit comments