CINXE.COM
Create generic Scala collections with CanBuildFrom
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Create generic Scala collections with CanBuildFrom</title> <meta name="viewport" content="width=device-width"> <link href="/assets/css/style.css" rel="stylesheet" /> <!-- favicon code start, by https://favicon.io/favicon-generator --> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> <link rel="manifest" href="/site.webmanifest"> <!-- favicon code end --> </head> <body> <div id="header-outer"> <header id="header"> <h1><a href="/">Michael Pollmeier ~ dev blog</a></h1> <nav> <ul> <li><a href="/">blog posts</a></li> <li><a href="/presentations">presentations</a></li> <li><a href="http://twitter.com/pollmeier">twitter</a></li> <li><a href="/feed.xml">rss</a></li> <li><a href="/about">about me</a></li> </ul> </nav> </header> </div> <div id="page"> <div id="content"> <article class="post"> <h1><a href="/create-generic-scala-collections-with-canbuildfrom">Create generic Scala collections with CanBuildFrom</a></h1> <p class="meta"> Posted on <span class="postdate">Aug 18, 2013</span><br/> tags: <!-- <a href="/tags.html#scala">scala</a> --> scala </p> <div class="post-content"><p>Normally if you write a function that takes a collection and returns a new collection that you create inside the function, you need to specify the concrete type of the collection. Otherwise, how are you going to create it? For example if you could do <em>val result = Seq(1,2,3)</em>, then your function returns either a Seq or an Iterable (the super type of all collections). But what if you want the caller of your function to determine which collection type to return? I.e. if I call that method with a Seq, I want it to return a Seq. If I call it with a List, it should create and return another List.</p> <p>The functions inside collection do that already. Take <em>map</em> for example: Seq[A].map(A ⇒ B) returns a Seq[B], while List[A].map(A ⇒ B) returns a List[B]. That means that inside the map function it creates a new instance of the collection type we called it on. How is that magic implemented? One could think that reflection might help here, but that gets us in all sorts of trouble: it’s slow, suffers from type erasure etc. Nope, in this case it’s actually implemented using an implicit value that’s every collection has in scope: <strong>CanBuildFrom</strong>.</p> <p>This is a really nice use case for implicits, as it allows us exactly what we wanted - create and fill an arbitrary collection - and is absolutely transparent for the caller. CanBuildFrom is basically a factory for any given collection that the compiler will fill in automatically for us.</p> <p>While you can easily see how it’s used within collection types itself, most of us will not define new or extend existing collection types. So how does it work if we want to use it outside of a collection implementation? The below reimplements <em>map</em> outside of a collection. I.e. map takes an arbitrary collection C[A] and a function that turns A ⇒ B. It creates an empty instance of the collection C[B] and fills it by calling the function A ⇒ B for each element.</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/** * Demonstrates usage of CanBuildFrom * by reimplementing map outside of a collection. * Takes a collection C[A] and a function A ⇒ B * Returns a colleciton C[B] */</span> <span class="k">def</span> <span class="nf">map</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span>, <span class="kt">C</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k"><:</span> <span class="kt">Iterable</span><span class="o">[</span><span class="kt">A</span><span class="o">]](</span><span class="n">collection</span><span class="k">:</span> <span class="kt">C</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="k">⇒</span> <span class="kt">B</span><span class="o">)(</span> <span class="k">implicit</span> <span class="n">cbf</span><span class="k">:</span> <span class="kt">CanBuildFrom</span><span class="o">[</span><span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span>, <span class="kt">B</span>, <span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]])</span><span class="k">:</span> <span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span> <span class="o">{</span> <span class="k">val</span> <span class="nv">builder</span> <span class="k">=</span> <span class="nf">cbf</span><span class="o">()</span> <span class="nv">builder</span><span class="o">.</span><span class="py">sizeHint</span><span class="o">(</span><span class="nv">collection</span><span class="o">.</span><span class="py">size</span><span class="o">)</span> <span class="n">collection</span> <span class="n">foreach</span> <span class="o">{</span> <span class="n">x</span> <span class="k">⇒</span> <span class="n">builder</span> <span class="o">+=</span> <span class="nf">f</span><span class="o">(</span><span class="n">x</span><span class="o">)</span> <span class="o">}</span> <span class="nv">builder</span><span class="o">.</span><span class="py">result</span> <span class="o">}</span> <span class="nf">map</span><span class="o">(</span><span class="nc">Seq</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">))</span> <span class="o">{</span> <span class="nv">_</span><span class="o">.</span><span class="py">toString</span> <span class="o">}</span> <span class="c1">//returns Seq[String]</span> <span class="nf">map</span><span class="o">(</span><span class="nc">List</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">))</span> <span class="o">{</span> <span class="nv">_</span><span class="o">.</span><span class="py">toString</span> <span class="o">}</span> <span class="c1">//returns List[String]</span> </code></pre></div></div> </div> <hr /> <div class="footer"> If you liked this post you may want to follow me on <a href="http://twitter.com/pollmeier">Twitter</a> and subscribe to the <a href="/feed.xml">RSS feed</a>. </div> </article> </div> </div> <script src="/assets/js/jquery.min.js"></script> <script src="/assets/js/jquery.mobilemenu.min.js"></script> <script> $(document).ready(function(){ $('#sidebar nav ul').mobileMenu({'topOptionText': 'Menu', 'prependTo': '#sidebar nav'}); }); </script> </body> </html>