Skip to content

Commit 9739081

Browse files
committed
Documentation on the "companion object extension" type class pattern
Closes #5694
1 parent 5d8e4fc commit 9739081

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

docs/docs/reference/other-new-features/extension-methods.md

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,33 +168,60 @@ that problem, it is recommended that the name of an extension method is
168168
preceded by a space and is also followed by a space if there are more parameters
169169
to come.
170170

171-
### Extension Methods and TypeClasses
171+
### Extension Methods and Type Classes
172172

173-
The rules for expanding extension methods make sure that they work seamlessly with typeclasses. For instance, consider `SemiGroup` and `Monoid`.
173+
The rules for expanding extension methods make sure that they work seamlessly with type classes. For instance, consider `SemiGroup` and `Monoid`.
174174
```scala
175-
// Two typeclasses:
175+
// Two type classes:
176176
trait SemiGroup[T] {
177177
def (x: T) combine(y: T): T
178178
}
179179
trait Monoid[T] extends SemiGroup[T] {
180180
def unit: T
181181
}
182-
183-
// An instance declaration:
184-
implicit object StringMonoid extends Monoid[String] {
185-
def (x: String) combine (y: String): String = x.concat(y)
186-
def unit: String = ""
182+
object Monoid {
183+
// An instance declaration:
184+
implicit object StringMonoid extends Monoid[String] {
185+
def (x: String) combine (y: String): String = x.concat(y)
186+
def unit: String = ""
187+
}
187188
}
188189

189190
// Abstracting over a typeclass with a context bound:
190191
def sum[T: Monoid](xs: List[T]): T =
191192
xs.foldLeft(implicitly[Monoid[T]].unit)(_.combine(_))
192193
```
194+
193195
In the last line, the call to `_.combine(_)` expands to `(x1, x2) => x1.combine(x2)`,
194196
which expands in turn to `(x1, x2) => ev.combine(x1, x2)` where `ev` is the implicit
195197
evidence parameter summoned by the context bound `[T: Monoid]`. This works since
196198
extension methods apply everywhere their enclosing object is available as an implicit.
197199

200+
To avoid having to write `implicitly[Monoid[T]].unit` to access the `unit` method in `Monoid[T]`,
201+
we can make `unit` itself an extension method on the `Monoid` _companion object_,
202+
as shown below:
203+
204+
```scala
205+
trait Monoid[T] extends SemiGroup[T] {
206+
def (self: Monoid.type) unit: T
207+
}
208+
object Monoid {
209+
implicit object StringMonoid extends Monoid[String] {
210+
def (x: String) combine (y: String): String = x.concat(y)
211+
def (self: Monoid.type) unit: String = ""
212+
}
213+
}
214+
```
215+
216+
This allows us to write `Monoid.unit` instead of `implicitly[Monoid[T]].unit`,
217+
letting the expected type distinguish which instance we want to use:
218+
219+
```scala
220+
def sum[T: Monoid](xs: List[T]): T =
221+
xs.foldLeft(Monoid.unit)(_.combine(_))
222+
```
223+
224+
198225
### Generic Extension Classes
199226

200227
As another example, consider implementations of an `Ord` type class with a `minimum` value:

0 commit comments

Comments
 (0)