12

I would like to implement method that takes arbitrary Seq[T] and returns Seq[T] as well. But when String is provided it should also return String.

Passing String works due to some implicit conversion from String to WrappedString extends IndexedSeq[Char], but I get Seq[Char] in return. Is it possible to get String back?

val sx: Seq[Int] = firstAndLast(List(1, 2, 3, 4))
val s1: Seq[Char] = firstAndLast("Foo Bar")
val s2: String = firstAndLast("Foo Bar")  //incompatible types error

def firstAndLast[T](seq: Seq[T]) = Seq(seq.head, seq.last)

firstAndLast() implementation is irrelevant, it is only an example.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674

1 Answers1

15

Yes, it is possible. You’ll have to require one of those fancy CanBuildFroms:

import scala.collection.generic.CanBuildFrom

def firstAndLast[CC, A, That](seq: CC)(implicit asSeq: CC => Seq[A], cbf: CanBuildFrom[CC, A, That]): That = {
  val b = cbf(seq)
  b.sizeHint(2)
  b += seq.head
  b += seq.last
  b.result
}

This will also work with arrays. Bonus: all lines in your example will compile and work as expected.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • I'm pretty sure it won't work with Arrays; you have no manifest defined – oxbow_lakes May 21 '12 at 18:06
  • 2
    It does work, because the needed `ClassManifest` is provided implicitly to the method delivering the appropriate `CanBuildFrom`, `scala.Array.canBuildFrom`. – Jean-Philippe Pellet May 21 '12 at 18:22
  • 2
    This should be easier with Miles' `FromRepr`, I guess. – Daniel C. Sobral May 21 '12 at 19:07
  • 3
    @DanielC.Sobral Sounds interesting; how about an example? – Jean-Philippe Pellet May 21 '12 at 20:04
  • 2
    @Jean-Philippe, thanks for your answer and the follow up comment. I was provoked into trying to find where the implicits for asSeq came from and discovered the following [command](http://blog.jetbrains.com/scala/2011/10/25/show-implicit-parameters-action/) Ctrl+Shift+P on IntelliJ which presents the actual implicit parameters at a call site. – Don Mackenzie May 21 '12 at 20:34
  • @Jean-PhilippePellet See [this pull request](https://github.com/scala/scala/pull/563). – Daniel C. Sobral May 22 '12 at 16:53
  • @Jean-PhilippePellet: thanks, works great for `List("abc", "def") map {firstAndLast(_)}` but fails for `List("abc", "def") map firstAndLast` (*error: No implicit view available from CC => Seq[A]. List("abc", "def") map firstAndLast*) - can your solution be tweaked to work here as well? I can open a new question if this is too broad for a comment. – Tomasz Nurkiewicz May 27 '12 at 11:53
  • @TomaszNurkiewicz I don’t know how to fix it. It probably has to do with the exact conditions in which the compiler is inserting the eta expansion itself… – Jean-Philippe Pellet May 27 '12 at 12:48
  • @Jean-PhilippePellet: thanks anyway! I created a follow-up: http://stackoverflow.com/questions/10774284. BTW your solution works with arrays as well. – Tomasz Nurkiewicz May 27 '12 at 13:21
  • I’ve just added the `b.sizeHint(2)` line. A micro-optimization for sure. – Jean-Philippe Pellet May 28 '12 at 14:31