2

Let's say I have a function:

def f(i: Int): String = i.toString

How can I define xxx such as (assume I can add methods to list without implicit conversions):

val source1: List[List[Int]] = List(List(1,2), List(3,4))
val source2: List[Int] = List(1,2)

val result1: List[List[String]] = source1.xxx(f)
val result2: List[String] = source2.xxx(f)

because with

List [ _ ]

I can't tell if _ can have map applied or not. Is it possible?

Luciano
  • 8,552
  • 5
  • 32
  • 56
  • So your `source` is typed as a `List[_]`? Or as `List[List[Int]]` or `List[Int]` as you have them here? – Luigi Plinge Jun 06 '13 at 17:08
  • Right now it's a List[A], but if A is a list (sequence, set..) I'd like to apply the function F to its elements instead of the list as a whole. – Luciano Jun 06 '13 at 18:43

3 Answers3

1

You could try this:

object Container1 {
  implicit class RichList1(l: List[List[Int]]) {
    def xxx(f: (Int) => String): List[List[String]] = {
      l.map(l => l.map(n => f(n)))
    }
  }
}

object Container2 {
  implicit class RichList2(l: List[Int]) {
    def xxx(f: (Int) => String): List[String] = {
      l.map(n => f(n))
    }
  }
}

object Mapping extends App {
  def f(i: Int): String = i.toString

  val source1: List[List[Int]] = List(List(1, 2), List(3, 4))
  val source2: List[Int] = List(1, 2)

  import Container1._
  val result1a: List[List[String]] = source1.xxx(f)
  println(result1a)

  import Container2._
  val result2a: List[String] = source2.xxx(f)
  println(result2a)

}
Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • Ok, implicit classes work, as long as I make one for each internal element (List[Int], Set[Int], etc). OTOH, I think that with list and set may be enough for my needs. – Luciano Jun 06 '13 at 19:11
  • Check my derived answer for one that works for any level of nesting without additional implicits. – Andy Jun 06 '13 at 20:47
1

It looks like you're trying to apply your function to the inner most item of the collection regardless of how deep you're values are nested (in this case, List[T] and List[List[T]]).

trait CanMapInner[WrappedV, WrappedB, V, B] {
  def mapInner(in: WrappedV, f: V ⇒ B): WrappedB
}

// simple base case (no nesting involved).
implicit def simpleMapper[V, B] = new CanMapInner[V, B, V, B] {
  def mapInner(in: V, f: (V) ⇒ B): B = f(in)
}

// drill down one level of "List".
implicit def wrappedMapper[V, B, InnerV, InnerB](implicit innerMapper: CanMapInner[InnerV, InnerB, V, B]) =
  new CanMapInner[List[InnerV], List[InnerB], V, B] {
    def mapInner(in: List[InnerV], f: (V) ⇒ B): List[InnerB] =
      in.map(innerMapper.mapInner(_, f))
  }

implicit class XXX[WrappedV](list: List[WrappedV]) {
  def xxx[V, B, WrappedB](f: V ⇒ B)(implicit mapper: CanMapInner[WrappedV, WrappedB, V, B]) = {
    list.map(inner ⇒ mapper.mapInner(inner, f))
  }
}

Adapted from qmajor's solution for Map.

Usage:

def f(i: Int): String = "Hello " + i.toString

val source1: List[List[Int]] = List(List(1, 2), List(3, 4))
val source2: List[Int] = List(1, 2)

val result1: List[List[String]] = source1.xxx(f)
val result2: List[String] = source2.xxx(f)

Console println source1
// > List(List(1, 2), List(3, 4))
Console println source2
// > List(1, 2)
Console println result1
// > List(List(Hello 1, Hello 2), List(Hello 3, Hello 4))
Console println result2
// > List(Hello 1, Hello 2)

I changed your f function for demo purposes.

Community
  • 1
  • 1
Andy
  • 5,108
  • 3
  • 26
  • 37
  • This one works with nested levels, but I find it more complicated than the other answer (which makes sense). Since I need only one level for now, I'll keep this answer as a reference. Thanks! – Luciano Jun 06 '13 at 21:18
  • It's definitely more complicated! – Andy Jun 06 '13 at 21:50
0

You could use shapeless' Scrap Your Boilerplate to do that. Example copied from readme:

val nested = List(Option(List(Option(List(Option(23))))))

val succ = everywhere(inc)(nested)
succ == List(Option(List(Option(List(Option(24))))))

Up to you if you are ready to include a library to do that.

gzm0
  • 14,752
  • 1
  • 36
  • 64
  • 1
    Nice try, but I can't make it work for this case: `object toStr extends Poly1 { implicit def caseInt = at[Int](_.toString) }` + `everywhere(toStr)(source1)` => `error: could not find implicit value for parameter c:...` – senia Jun 06 '13 at 17:29
  • Since I'm using Spray, I already have shapeless (maybe an older version). However, as much I read about it, I still not sure how to use that library. – Luciano Jun 06 '13 at 19:09
  • @senia Ouch... Maybe another of those implicits which cannot be found (e.g. http://stackoverflow.com/questions/16703527/tolist-on-shapeless-hlist-fails-when-resulting-existential-type-too-complex). I'll give it a try – gzm0 Jun 06 '13 at 19:48