Skip to content

Add code tabs for _tour/compound-types #2552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 27, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 54 additions & 10 deletions _tour/compound-types.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: tour
title: Compound Types
title: Intersection Types, aka Compound Types
partof: scala-tour

num: 26
Expand All @@ -10,42 +10,86 @@ previous-page: abstract-type-members
redirect_from: "/tutorials/tour/compound-types.html"
---

Sometimes it is necessary to express that the type of an object is a subtype of several other types. In Scala this can be expressed with the help of *compound types*, which are intersections of object types.
Sometimes it is necessary to express that the type of an object is a subtype of several other types.

In Scala this can be expressed with the help of *intersection types*, (or *compound types* in
Scala 2) which are types that behave like any part of the intersection.

Suppose we have two traits `Cloneable` and `Resetable`:

{% tabs compound-types_1 class=tabs-scala-version %}
{% tab 'Scala 2' for=compound-types_1 %}
```scala mdoc
trait Cloneable extends java.lang.Cloneable {
override def clone(): Cloneable = {
override def clone(): Cloneable = { // makes clone public
super.clone().asInstanceOf[Cloneable]
}
}
trait Resetable {
def reset: Unit
}
```
{% endtab %}
{% tab 'Scala 3' for=compound-types_1 %}
```scala
trait Cloneable extends java.lang.Cloneable:
override def clone(): Cloneable = // makes clone public
super.clone().asInstanceOf[Cloneable]
trait Resetable:
def reset: Unit
```
{% endtab %}
{% endtabs %}

Now suppose we want to write a function `cloneAndReset` which takes an object, clones it and resets the original object:

```
{% tabs compound-types_2 class=tabs-scala-version %}
{% tab 'Scala 2' for=compound-types_2 %}
```scala mdoc:fail
def cloneAndReset(obj: ?): Cloneable = {
val cloned = obj.clone()
obj.reset
cloned
}
```
{% endtab %}
{% tab 'Scala 3' for=compound-types_2 %}
```scala
def cloneAndReset(obj: ?): Cloneable =
val cloned = obj.clone()
obj.reset
cloned
```
{% endtab %}
{% endtabs %}

The question arises what the type of the parameter `obj` is. If it's `Cloneable` then the object can be `clone`d, but not `reset`; if it's `Resetable` we can `reset` it, but there is no `clone` operation. To avoid type casts in such a situation, we can specify the type of `obj` to be both `Cloneable` and `Resetable`. This compound type is written like this in Scala: `Cloneable with Resetable`.
The question arises what the type of the parameter `obj` is. If it's `Cloneable` then the object can be `clone`d, but not `reset`; if it's `Resetable` we can `reset` it, but there is no `clone` operation. To avoid type casts in such a situation, we can specify the type of `obj` to be both `Cloneable` and `Resetable`.
{% tabs compound-types_3 class=tabs-scala-version %}
{% tab 'Scala 2' for=compound-types_3 %}
This compound type is written in Scala as `Cloneable with Resetable`.

Here's the updated function:

```
```scala mdoc:fail
def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {
//...
}
```
Note that you can have more than two types: `A with B with C with ...`.
This means the same as thing as `(...(A with B) with C) with ... )`
{% endtab %}
{% tab 'Scala 3' for=compound-types_3 %}
This intersection type is written in Scala as `Cloneable & Resetable`.

Compound types can consist of several object types and they may have a single refinement which can be used to narrow the signature of existing object members.
The general form is: `A with B with C ... { refinement }`
Here's the updated function:
```scala
def cloneAndReset(obj: Cloneable & Resetable): Cloneable = {
//...
}
```
<!-- Compound types can consist of several object types and they may have a single refinement which can be used to narrow the signature of existing object members. -->
Note that you can have more than two types: `A & B & C & ...`.
And `&` is associative, so parentheses can be added around any part without changing the meaning.
{% endtab %}
{% endtabs %}

An example for the use of refinements is given on the page about [class composition with mixins](mixin-class-composition.html).
<!-- An example for the use of refinements is given on the page about [class composition with mixins](mixin-class-composition.html). -->