Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>What you're running into is not necessarily <code>CanBuildFrom</code> itself, or the <code>Array</code> vs. <code>Seq</code> issue. You're running into <code>String</code> which is <em>not</em> higher-kinded, but supports <code>map</code> against its <code>Char</code>s. </p> <p><strong>SO: First a digression into Scala's collection design.</strong></p> <p>What you need is a way to infer both the collection type (e.g. <code>String</code>, <code>Array[Int]</code>, <code>List[Foo]</code>) and the element type (e.g. <code>Char</code>, <code>Int</code>, <code>Foo</code> corresponding to the above).</p> <p>Scala 2.10.x has added a few "type classes" to help you. For example, you can do the following:</p> <pre><code>class FilterMapImpl[A, Repr](val r: GenTraversableLike[A, Repr]) { final def filterMap[B, That](f: A =&gt; Option[B])(implicit cbf: CanBuildFrom[Repr, B, That]): That = r.flatMap(f(_).toSeq) } implicit def filterMap[Repr, A](r: Repr)(implicit fr: IsTraversableOnce[Repr]): FilterMapImpl[fr.A,Repr] = new FilterMapImpl(fr.conversion(r)) </code></pre> <p>There's two pieces here. FIRST, your class that uses collections needs two type parameters: The specific type of the collection <code>Repr</code> and the type of the elements <code>A</code>.</p> <p>Next, you define an implicit method which only takes the collection type <code>Repr</code>. You use the <code>IsTraversableOnce</code> (note: there is also an <code>IsTraversableLike</code>) to capture the element type of that collection. You see this used in the type signature <code>FilterMapImpl[Repr, fr.A]</code>. </p> <p>Now, part of this is because Scala does not use the same category for all of its "functor-like" operations. Specifically, <code>map</code> is a useful method for <code>String</code>. I can adjust all characters. However, <code>String</code> can only be a <code>Seq[Char]</code>. If I want to define a <code>Functor</code>, then my category can only contain the type <code>Char</code> and the arrows <code>Char =&gt; Char</code>. This logic is captured in <code>CanBuildFrom</code>. However, since a <code>String</code> is a <code>Seq[Char]</code>, if you try to use a <code>map</code> in the category supported by <code>Seq</code>'s <code>map</code> method, then <code>CanBuildFrom</code> will alter your call to <code>map</code>.</p> <p>We're essentially defining an "inheritance" relationship for our categories. If you try to use the <code>Functor</code> pattern, we drop the type signature to the most specific category we can retain. Call it what you will; that's a big motivating factor for the current collection design.</p> <p><strong>End Digression, answer the question</strong></p> <p>Now, because we're trying to infer a <em>lot</em> of types at the same time, I think this option has the fewest type annotations:</p> <pre><code>import collection.generic._ def map[Repr](col: Repr)(implicit tr: IsTraversableLike[Repr]) = new { def apply[U, That](f: tr.A =&gt; U)(implicit cbf: CanBuildFrom[Repr, U, That]) = tr.conversion(col) map f } scala&gt; map("HI") apply (_ + 1 toChar ) warning: there were 2 feature warnings; re-run with -feature for details res5: String = IJ </code></pre> <p>The important piece to note here is that <code>IsTraversableLike</code> captures a conversion from <code>Repr</code> to <code>TraversableLike</code> that allows you to use the <code>map</code> method.</p> <p><strong>Option 2</strong></p> <p>We also split the method call up a bit so that Scala can infer the types <code>Repr</code> and <code>U</code> before we define our anonymous function. To avoid type annotations on anonymous functions, we must have all types <em>known</em> before it shows up. Now, we can still have Scala infer some types, but lose things that are <em>implicitly</em> <code>Traversable</code> if we do this:</p> <pre><code>import collection.generic._ import collection._ def map[Repr &lt;: TraversableLike[A, Repr], A, U, That](col: Repr with TraversableLike[A,Repr])(f: A =&gt; U)(implicit cbf: CanBuildFrom[Repr, U, That]) = col map f </code></pre> <p>Notice that we have to use <code>Repr with TraversableLike[A,Repr]</code>. It seems that most F-bounded types require this juggling.</p> <p>In any case, now let's see what happens on something that extends <code>Traversable</code>:</p> <pre><code>scala&gt; map(List(40,41))(_ + 1 toChar ) warning: there were 1 feature warnings; re-run with -feature for details res8: List[Char] = List(), *) </code></pre> <p>That's great. However, if we want the same usage for <code>Array</code> and <code>String</code>, we have to go to a bit more work:</p> <pre><code>scala&gt; map(Array('H', 'I'): IndexedSeq[Char])(_ + 1 toChar)(breakOut): Array[Char] warning: there were 1 feature warnings; re-run with -feature for details res14: Array[Char] = Array(I, J) scala&gt; map("HI": Seq[Char])(_ + 1 toChar)(breakOut) : String warning: there were 1 feature warnings; re-run with -feature for details res11: String = IJ </code></pre> <p>There are two pieces to this usage:</p> <ol> <li>We have to use a type annotation for the implicit conversion from <code>String</code>/<code>Array</code> → <code>Seq</code>/<code>IndexedSeq</code>.</li> <li>We have to use <code>breakOut</code> for our <code>CanBuildFrom</code> and type-annotate the expected return value.</li> </ol> <p>This is solely because the type <code>Repr &lt;: TraversableLike[A,Repr]</code> does not include <code>String</code> or <code>Array</code>, since those use implicit conversions.</p> <p><strong>Option 3</strong></p> <p>You can place all the implicits together at the end and require the user to annotate types. Not the most elegant solution, so I think I'll avoid posting it unless you'd really like to see it.</p> <p>SO, basically if you want to include <code>String</code> and <code>Array[T]</code> as collections, you have to jump through some hoops. This category restriction for map applies to both <code>String</code> and <code>BitSet</code> functors in Scala. </p> <p>I hope that helps. Ping me if you have any more questions.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload