Skip to content

Add code tabs for _tour/mixin-class-composition #2524

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 6 commits into from
Sep 15, 2022
Merged
Changes from 4 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
93 changes: 93 additions & 0 deletions _tour/mixin-class-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ redirect_from: "/tutorials/tour/mixin-class-composition.html"
---
Mixins are traits which are used to compose a class.

{% tabs mixin-first-exemple class=tabs-scala-version %}

{% tab 'Scala 2' for=mixin-first-exemple %}
```scala mdoc
abstract class A {
val message: String
Expand All @@ -30,19 +33,60 @@ println(d.loudMessage) // I'M AN INSTANCE OF CLASS B
```
Class `D` has a superclass `B` and a mixin `C`. Classes can only have one superclass but many mixins (using the keywords `extends` and `with` respectively). The mixins and the superclass may have the same supertype.

{% endtab %}

{% tab 'Scala 3' for=mixin-first-exemple %}
```scala
abstract class A:
val message: String
class B extends A:
val message = "I'm an instance of class B"
trait C extends A:
def loudMessage = message.toUpperCase()
class D extends B, C

val d = D()
println(d.message) // I'm an instance of class B
println(d.loudMessage) // I'M AN INSTANCE OF CLASS B
```
Class `D` has a superclass `B` and a mixin `C`. Classes can only have one superclass but many mixins (using the keyword `extends` and the separator `,` respectively). The mixins and the superclass may have the same supertype.

{% endtab %}

{% endtabs %}

Now let's look at a more interesting example starting with an abstract class:

{% tabs mixin-abstract-iterator class=tabs-scala-version %}

{% tab 'Scala 2' for=mixin-abstract-iterator %}
```scala mdoc
abstract class AbsIterator {
type T
def hasNext: Boolean
def next(): T
}
```
{% endtab %}

{% tab 'Scala 3' for=mixin-abstract-iterator %}
```scala
abstract class AbsIterator:
type T
def hasNext: Boolean
def next(): T
```
{% endtab %}

{% endtabs %}

The class has an abstract type `T` and the standard iterator methods.

Next, we'll implement a concrete class (all abstract members `T`, `hasNext`, and `next` have implementations):

{% tabs mixin-concrete-string-iterator class=tabs-scala-version %}

{% tab 'Scala 2' for=mixin-concrete-string-iterator %}
```scala mdoc
class StringIterator(s: String) extends AbsIterator {
type T = Char
Expand All @@ -55,24 +99,73 @@ class StringIterator(s: String) extends AbsIterator {
}
}
```
{% endtab %}

{% tab 'Scala 3' for=mixin-concrete-string-iterator %}
```scala
class StringIterator(s: String) extends AbsIterator:
type T = Char
private var i = 0
def hasNext = i < s.length
def next() =
val ch = s charAt i
i += 1
ch
```
{% endtab %}

{% endtabs %}

`StringIterator` takes a `String` and can be used to iterate over the String (e.g. to see if a String contains a certain character).

Now let's create a trait which also extends `AbsIterator`.

{% tabs mixin-extended-abstract-iterator class=tabs-scala-version %}

{% tab 'Scala 2' for=mixin-extended-abstract-iterator %}
```scala mdoc
trait RichIterator extends AbsIterator {
def foreach(f: T => Unit): Unit = while (hasNext) f(next())
}
```
This trait implements `foreach` by continually calling the provided function `f: T => Unit` on the next element (`next()`) as long as there are further elements (`while (hasNext)`). Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator.

{% endtab %}

{% tab 'Scala 3' for=mixin-extended-abstract-iterator %}
```scala mdoc
trait RichIterator extends AbsIterator:
def foreach(f: T => Unit): Unit = while hasNext do f(next())
}
```
This trait implements `foreach` by continually calling the provided function `f: T => Unit` on the next element (`next()`) as long as there are further elements (`while hasNext`). Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator.

{% endtab %}

{% endtabs %}

We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class.

{% tabs mixin-combination-class class=tabs-scala-version %}

{% tab 'Scala 2' for=mixin-combination-class %}
```scala mdoc
class RichStringIter extends StringIterator("Scala") with RichIterator
val richStringIter = new RichStringIter
richStringIter.foreach(println)
```
{% endtab %}

{% tab 'Scala 3' for=mixin-combination-class %}
```scala mdoc
class RichStringIter extends StringIterator("Scala"), RichIterator
val richStringIter = RichStringIter()
richStringIter.foreach(println)
```
{% endtab %}

{% endtabs %}

The new class `RichStringIter` has `StringIterator` as a superclass and `RichIterator` as a mixin.

With single inheritance we would not be able to achieve this level of flexibility.