@@ -3,131 +3,77 @@ layout: doc-page
33title : " Programmatic Structural Types"
44---
55
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 = {
9545 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+ ```
9651
97- Then ` person.name ` will have static type ` String ` , and will produce ` "Emma" ` as result.
52+ ## Extensibility
9853
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.
10457
105- ### Notes:
58+ ## Relation with ` scala.Dynamic `
10659
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
11361both select members programmatically. But there are also some
11462differences.
11563
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.
12367
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.
13071
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.
13278
133- For more info, see [ Issue # 1886 ] ( https://github.com/lampepfl/dotty/issues/1886 ) .
79+ [ More details ] ( structural-types-spec.html )
0 commit comments