Skip to content

Commit 5b90c3f

Browse files
committed
Add a section about breakOut
1 parent 4eb0729 commit 5b90c3f

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

blog/_posts/2017-05-29-tribulations-canbuildfrom.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,54 @@ could be supported even without implicit builders: you could just use an existin
190190
instance and navigate through its companion object (providing the builder), or you could just
191191
use the companion object directly to get a builder.
192192

193+
## `breakOut` escape hatch
194+
195+
As we have previously seen, in the current collections when we want to transform some
196+
collection into a new collection, we rely on an available implicit `CanBuildFrom`
197+
instance to get a builder for the target collection. The implicit search is
198+
driven by the type of the initial collection and the type of elements of the target
199+
collection. The available implicit instances have been designed to make sense in the most
200+
common cases.
201+
202+
However, sometimes this default behavior is not what you want. For instance, consider the
203+
following program:
204+
205+
~~~ scala
206+
val xs: List[Int] = 1 :: 2 :: 3 :: Nil
207+
val xsWithSquares: Map[Int, Int] =
208+
xs.map(x => (x, x * x))
209+
~~~
210+
211+
If you try to compile it you will get a compile error because the implicitly
212+
resolved builder produces a `List[(Int, Int)]` instead of the desired `Map[Int, Int]`.
213+
We could convert this `List[(Int, Int)]` into a `Map[Int, Int]` but that
214+
would be inefficient for large collections.
215+
216+
We can fix this issue by using the `breakOut` escape hatch:
217+
218+
~~~ scala
219+
val xs: List[Int] = 1 :: 2 :: 3 :: Nil
220+
val xsWithSquares: Map[Int, Int] =
221+
xs.map(x => (x, x * x))(collection.breakOut)
222+
~~~
223+
224+
`breakOut` selects a `CanBuildFrom` instance irrespective of the initial collection type.
225+
In our case the `Map[Int, Int]` type annotation fixes the target type of the builder
226+
to implicitly look for.
227+
228+
In the new design we have no direct equivalent of `breakOut`. The solution of the
229+
above example consists in using a `View` to avoid the construction of an
230+
intermediate collection:
231+
232+
~~~ scala
233+
val xs: List[Int] = 1 :: 2 :: 3 :: Nil
234+
val xsWithSquares: Map[Int, Int] =
235+
xs.view.map(x => (x, x * x)).to(Map)
236+
~~~
237+
238+
In practice, we expect that most usages of `breakOut` could be adapted to the new design by using
239+
a `View` followed by an explicit `to` call. However, this is an area that remains to explore.
240+
193241
## Summary
194242

195243
In this article we have reviewed the features built on top of `CanBuildFrom` and explained

0 commit comments

Comments
 (0)