Skip to content

Commit f3ae927

Browse files
authored
Merge pull request #2550 from flomebul/inner-classes
Add code tabs for _tour/inner-classes
2 parents 534965a + af38c49 commit f3ae927

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

_tour/inner-classes.md

+51
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ In Scala it is possible to let classes have other classes as members. As opposed
1414

1515
To illustrate the difference, we quickly sketch the implementation of a graph datatype:
1616

17+
{% tabs inner-classes_1 class=tabs-scala-version %}
18+
{% tab 'Scala 2' for=inner-classes_1 %}
1719
```scala mdoc
1820
class Graph {
1921
class Node {
@@ -32,8 +34,29 @@ class Graph {
3234
}
3335
}
3436
```
37+
{% endtab %}
38+
{% tab 'Scala 3' for=inner-classes_1 %}
39+
```scala
40+
class Graph:
41+
class Node:
42+
var connectedNodes: List[Node] = Nil
43+
def connectTo(node: Node): Unit =
44+
if !connectedNodes.exists(node.equals) then
45+
connectedNodes = node :: connectedNodes
46+
47+
var nodes: List[Node] = Nil
48+
def newNode: Node =
49+
val res = Node()
50+
nodes = res :: nodes
51+
res
52+
```
53+
{% endtab %}
54+
{% endtabs %}
55+
3556
This program represents a graph as a list of nodes (`List[Node]`). Each node has a list of other nodes it's connected to (`connectedNodes`). The `class Node` is a _path-dependent type_ because it is nested in the `class Graph`. Therefore, all nodes in the `connectedNodes` must be created using the `newNode` from the same instance of `Graph`.
3657

58+
{% tabs inner-classes_2 %}
59+
{% tab 'Scala 2 and 3' for=inner-classes_2 %}
3760
```scala mdoc
3861
val graph1: Graph = new Graph
3962
val node1: graph1.Node = graph1.newNode
@@ -42,11 +65,16 @@ val node3: graph1.Node = graph1.newNode
4265
node1.connectTo(node2)
4366
node3.connectTo(node1)
4467
```
68+
{% endtab %}
69+
{% endtabs %}
70+
4571
We have explicitly declared the type of `node1`, `node2`, and `node3` as `graph1.Node` for clarity but the compiler could have inferred it. This is because when we call `graph1.newNode` which calls `new Node`, the method is using the instance of `Node` specific to the instance `graph1`.
4672

4773
If we now have two graphs, the type system of Scala does not allow us to mix nodes defined within one graph with the nodes of another graph, since the nodes of the other graph have a different type.
4874
Here is an illegal program:
4975

76+
{% tabs inner-classes_3 %}
77+
{% tab 'Scala 2 and 3' for=inner-classes_3 %}
5078
```scala mdoc:fail
5179
val graph1: Graph = new Graph
5280
val node1: graph1.Node = graph1.newNode
@@ -56,8 +84,13 @@ val graph2: Graph = new Graph
5684
val node3: graph2.Node = graph2.newNode
5785
node1.connectTo(node3) // illegal!
5886
```
87+
{% endtab %}
88+
{% endtabs %}
89+
5990
The type `graph1.Node` is distinct from the type `graph2.Node`. In Java, the last line in the previous example program would have been correct. For nodes of both graphs, Java would assign the same type `Graph.Node`; i.e. `Node` is prefixed with class `Graph`. In Scala such a type can be expressed as well, it is written `Graph#Node`. If we want to be able to connect nodes of different graphs, we have to change the definition of our initial graph implementation in the following way:
6091

92+
{% tabs inner-classes_4 class=tabs-scala-version %}
93+
{% tab 'Scala 2' for=inner-classes_4 %}
6194
```scala mdoc:nest
6295
class Graph {
6396
class Node {
@@ -76,3 +109,21 @@ class Graph {
76109
}
77110
}
78111
```
112+
{% endtab %}
113+
{% tab 'Scala 3' for=inner-classes_4 %}
114+
```scala
115+
class Graph:
116+
class Node:
117+
var connectedNodes: List[Graph#Node] = Nil
118+
def connectTo(node: Graph#Node): Unit =
119+
if !connectedNodes.exists(node.equals) then
120+
connectedNodes = node :: connectedNodes
121+
122+
var nodes: List[Node] = Nil
123+
def newNode: Node =
124+
val res = Node()
125+
nodes = res :: nodes
126+
res
127+
```
128+
{% endtab %}
129+
{% endtabs %}

0 commit comments

Comments
 (0)