0

How to use the map method in the Iterable trait in the example below?

As I understand this method will return a function which I have to call to execute internal logic.

trait Container[E] {
    def += (e: E): Unit
}

trait Iterable[E, C[X] <: Container[X]] 
{
    def iterator(): Iterator[E]
    def build[F](): C[F]

    def map[F](f : (E) => F) : C[F] = {
        val res = build[F]()
        val iter = iterator()
        while (iter.hasNext) res += f(iter.next())
        res
    }

}

class Buffer[T] extends Container[T]
{
    val list = scala.collection.mutable.ListBuffer.empty[T]
    def Children = list
    def += (e: T): Unit = list += e
}   


class Range(val low: Int, val high: Int) extends Iterable[Int, Buffer] {

    def iterator() = new Iterator[Int] 
    {
        private var i = low
        def hasNext = i <= high
        def next() = { i += 1; i - 1 }
    }
    def build[F]() = new Buffer[F]
}

val range = new Range(1, 3)
var list = range.map[String](_)
Mifeet
  • 12,949
  • 5
  • 60
  • 108
Pavel
  • 1,519
  • 21
  • 29
  • What is your problem? What output do you expect compared to what you actually get? As your question is formulated now, it is difficult to tell what you would like to know. – Mifeet Mar 14 '16 at 12:38
  • well, I agree about question. Probably need to spend a bit more time to read theory! I don't understand what is passed as a parameters to map method so not sure how to use it. Need some simple example! – Pavel Mar 14 '16 at 13:13
  • Looks like question title is a bit wrong. The question is not related to higher-kinded types as such. – vitalii Mar 14 '16 at 13:26

2 Answers2

1
new Range(2,5).map(_.toString)
vitalii
  • 3,335
  • 14
  • 18
  • So this could be any function which will return string as result? – Pavel Mar 14 '16 at 13:14
  • In fact it accepts any function that maps from range type - in this particular instance int - to any other type R. And the result is the container of type R, in this case Range[R]. If you provide function int->String, you'll get Range[String] as result. – vitalii Mar 14 '16 at 13:25
  • I do understand your answer just need a bit more detailed answer like below/above. I am just a beginner. Thank you ! – Pavel Mar 14 '16 at 13:35
1

The method in question has the following signature:

trait Iterable[E, C[X] <: Container[X]] {
  def map[F](f : (E) => F) : C[F]
  // ...
}

First, let's look at the type of f argument. The signature (E) => F says that f is a function which takes a single argument of type E and returns a value of type F. Any function (or method) with this signature can be passed to map() as argument. See also Scala documentation.

Another important thing to understand is that the map function is generic with type parameter F. Value for this type parameter can either be specified manually or inferred by the compiler from the argument passed to map:

new Range(1,2).map[String](_.toString) // F is String
// new Range(1,2).map[Int](_.toString) // F is Int, compilation will fail

val mapFunction: Int => String = _.toString
new Range(1,2).map(mapFunction) //  mapFunction is a function from Int to String,
                                // the compiler infers F is String

Basically, e.g. with Range, you can pass to the map() function any function which takes a single Int parameter (because Range binds E to Int) and returns anything (except for Unit). A few more examples:

val r = Range(1,2)
val v1: Buffer[String] = r.map(_.toString)
val v2: Buffer[Int] = r.map(i => i + 1)
val v3: Buffer[Double] = r.map(Int.int2double)
val i: Int = 1
val v4: Buffer[Int] = r.map(i.max)

As you can see, map() returns type Buffer[F] because that's what Range binds to the C[X] type parameter.


As @vitalii noted, the question is not related to higher-kinded types. For more information about those, check out other questions or blogs.

Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108