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
An intuitive, readable, and extendible way to deconstruct case classes in pattern matching.
18
18
19
+
// TODO: many times requested, find more references than the contributors thread
20
+
21
+
// TODO: use lingo of spec
22
+
19
23
## Motivating Examples
20
24
21
25
Given one wants to use pattern matching on a case class:
@@ -27,9 +31,6 @@ val user = User(name = "Anna", age = 10, city = "Berlin")
27
31
28
32
valannasCity= user match
29
33
caseUser(name ="Anna", city = c) => c
30
-
// wild stuff:
31
-
caseUser(city = city, name =s"To$_") =>???
32
-
caseUser(name = guy @ ("Guy"|"guy")) =>???
33
34
```
34
35
35
36
The Deconstruction allows the same syntax as the construction and seems to be what people intuitive expect. See //TODO: Examples
@@ -41,12 +42,9 @@ Without names in patterns user have to use underscore a lot. The example above w
41
42
```scala
42
43
valannasCity= user match
43
44
caseUser("Anna", _, c) => c
44
-
// wild stuff:
45
-
caseUser(s"To$_", _, city) =>???
46
-
caseUser(guy @ ("Guy"|"guy"), _, _) =>???
47
45
```
48
46
49
-
This makes it hard which parameter means what, basically the same idea as for named arguments. (The IDE can help here)
47
+
This makes it hard which parameter means what, basically the same idea as for named arguments. (IDEs help here, by showing the names.)
50
48
51
49
In addition, it breaks every time a field of `User` gets added, rearranged, or removed.
52
50
In the worst case it breaks silently, if two fields with the type switch places.
@@ -73,6 +71,21 @@ Goal is similarity between construction and deconstruction of case classes.
73
71
74
72
Before this was invalid syntax, so this shouldn't affect any existing Scala program.
75
73
74
+
### Desugaring
75
+
76
+
> One important principle of Scala’s pattern matching design is that case classes should be abstractable. I.e. we want to be able to represent the abilities of a case class without exposing the case class itself. That also allows code to evolve from a case class to a regular class or a different case class while maintaining the interface of the old case class. [Martin Odersky](https://contributors.scala-lang.org/t/pattern-matching-with-named-fields/1829/52)
77
+
78
+
To create a user defined destructor user the `@names` annotation.
Mixed patterns, with positional and named patterns are allowed to keep the similarity.
@@ -83,11 +96,28 @@ But they have no motivational use case. Maybe they should be disallowed.
83
96
caseUser(_, city = c) =>// Leading underscore are espacially useless
84
97
```
85
98
99
+
### Disallow same name twice
100
+
101
+
// TODO: Motivate this restriction
102
+
103
+
```scala
104
+
caseUser(city = city1, city = city2) => a // error city is used twice
105
+
caseUser(name1, name = name2) => a // error city is used twice
106
+
```
107
+
108
+
### Order of name and term
109
+
110
+
Normally an equal sign assigns the result of the right side to the left side. With the proposed syntax that's not the case. However, for most, if not all, people that's the correct. The order seems the requires a look ahead in the parser.
111
+
112
+
// TODO: Performance test
113
+
86
114
## Implementation
87
115
88
116
89
117
'Simple' rewrite of patterns. If a pattern with a name is encountered, the compiler looks up the index of those names and places the tree accordingly.
90
118
119
+
// TODO: Brag about the simplicity of the implementation
120
+
91
121
Example:
92
122
93
123
```scala
@@ -104,18 +134,16 @@ case User(
104
134
105
135
## Drawbacks
106
136
107
-
Without allowing user defined named arguments in pattern matching, the fact that class is a case class becomes part if it's public interface. Changing a case class to a normal class is a backward incompatible change, that library maintainers of to be aware. This is especially worrying since currently libraries where designed without this feature in mind.
Whenever a single named argument is used in pattern, the pattern can have fewer arguments than the unapply.
141
+
This leads to inconsistency, as pointed out by Lionel Parreaux in the [Scala Contributors Thread](https://contributors.scala-lang.org/t/pattern-matching-with-named-fields/1829/44). This could lead users to use a named pattern, just to skip all parameters.
142
+
143
+
```scala
144
+
caseUser(age = _) =>"Just wanted to use the extractor, lol!"
116
145
```
117
146
118
-
This limitation could be overcome by Named Tuple Arguments, discussed below.
119
147
120
148
## Alternatives
121
149
@@ -138,26 +166,42 @@ User(10) match
138
166
Libraries like [Monocle][monocle] could be extended to reduce the boilerplate, but still some boilerplate would remain.
139
167
In addition, this breaks the intuitive similarity between construction and deconstruction.
### Named Tuple Arguments / Anonymous Case Classes
142
181
143
182
This was mentioned in the discussion about [Named Tuple Arguments / Anonymous Case Classes][named-tuple] as bonus, that named tuples could transport the names from unapply to the pattern.
144
183
This would be more generic and could handle user defined extractors.
145
184
146
-
However this isn't much of an alternative, but more of a generalization.
147
-
148
185
### Partial destructuring in guards
149
186
150
-
Lionel Parreaux proposed a more powerful mechanism:
Lionel Parreaux proposed a more powerful mechanism, where if guards of cases could them self contains destructuring patterns.
188
+
189
+
```scala
190
+
caseuser: UserifAge(years) <- user => years
191
+
caseUser(age =Age(years)) => years // both cases do the same thing
192
+
```
152
193
194
+
His proposal is striclty more powerfull, but arguably less intuitive. Both, Pattern matching with named fields and Partial destructuring in guards could be implemented along each other. Named fields for simple patterns and destructuring in guards for complex patterns. However, they offer two ways to do the same thing and could lead to lots of bike shedding.
153
195
154
196
## References
155
197
156
198
*[Scala Contributors Thread][contributors-thread]
157
199
158
200
*[Monocle][monocle]
159
201
*[Named Tuple Arguments / Anonymous Case Classes][named-tuple]
202
+
*[Partial Destructuring in Guards][partial-destructuring-in-guards]
0 commit comments