-
Notifications
You must be signed in to change notification settings - Fork 1k
Rewrote Mixin-class composition tour #738
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fixed broken link closes #771
- Loading branch information
commit 5239edcbff73733afd48052cbff0436f24779d21
There are no files selected for viewing
82 changes: 54 additions & 28 deletions
82
tutorials/tour/_posts/2017-02-13-mixin-class-composition.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,81 @@ | ||
--- | ||
layout: tutorial | ||
title: Mixin Class Composition | ||
title: Class Composition with Mixins | ||
|
||
disqus: true | ||
|
||
tutorial: scala-tour | ||
categories: tour | ||
num: 6 | ||
next-page: anonymous-function-syntax | ||
next-page: higher-order-functions | ||
previous-page: traits | ||
prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types | ||
--- | ||
Mixins are traits which are used to compose a class. | ||
|
||
```tut | ||
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 with C | ||
|
||
val d = new D | ||
d.message // I'm an instance of class B | ||
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. | ||
|
||
Now let's look at a more interesting example starting with an abstract class: | ||
|
||
As opposed to languages that only support _single inheritance_, Scala has a more general notion of class reuse. Scala makes it possible to reuse the _new member definitions of a class_ (i.e. the delta in relationship to the superclass) in the definition of a new class. This is expressed as a _mixin-class composition_. Consider the following abstraction for iterators. | ||
|
||
```tut | ||
abstract class AbsIterator { | ||
type T | ||
def hasNext: Boolean | ||
def next: T | ||
} | ||
``` | ||
|
||
Next, consider a mixin class which extends `AbsIterator` with a method `foreach` which applies a given function to every element returned by the iterator. To define a class that can be used as a mixin we use the keyword `trait`. | ||
|
||
```tut | ||
trait RichIterator extends AbsIterator { | ||
def foreach(f: T => Unit) { while (hasNext) f(next) } | ||
} | ||
``` | ||
|
||
Here is a concrete iterator class, which returns successive characters of a given string: | ||
|
||
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): | ||
|
||
```tut | ||
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 } | ||
def hasNext = i < s.length | ||
def next = { | ||
val ch = s charAt i | ||
i += 1 | ||
ch | ||
} | ||
} | ||
``` | ||
|
||
We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class. With single inheritance and interfaces alone this is impossible, as both classes contain member implementations with code. Scala comes to help with its _mixin-class composition_. It allows the programmers to reuse the delta of a class definition, i.e., all new definitions that are not inherited. This mechanism makes it possible to combine `StringIterator` with `RichIterator`, as is done in the following test program which prints a column of all the characters of a given string. | ||
|
||
`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`. | ||
|
||
```tut | ||
object StringIteratorTest { | ||
def main(args: Array[String]) { | ||
class Iter extends StringIterator(args(0)) with RichIterator | ||
val iter = new Iter | ||
iter foreach println | ||
} | ||
trait RichIterator extends AbsIterator { | ||
def foreach(f: T => Unit): Unit = while (hasNext) f(next) | ||
} | ||
``` | ||
|
||
The `Iter` class in function `main` is constructed from a mixin composition of the parents `StringIterator` and `RichIterator` with the keyword `with`. The first parent is called the _superclass_ of `Iter`, whereas the second (and every other, if present) parent is called a _mixin_. | ||
Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator. | ||
|
||
We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class. | ||
|
||
```tut | ||
object StringIteratorTest extends App { | ||
class Iter extends StringIterator(args(0)) with RichIterator | ||
val iter = new Iter | ||
iter foreach println | ||
} | ||
``` | ||
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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.