Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala map on iterator does not produce side effects

Why is it that,

scala> List(1,2,3,4).iterator.map((x: Int) => println(x))

does not print out

1
2
3
4

while

List(1,2,3,4).map((x: Int) => println(x))
List(1,2,3,4).foreach((x: Int) => println(x))
List(1,2,3,4).iterator.foreach((x: Int) => println(x))

all do?

In other words, why is it that a map on a iterator that maps type T to Unit and has side effects unable to show those side effects?

Edit:

Also why does the following invocation of lazyMap actually computes the new iterator (provide the complete new iterator) from beginning to end if iterator is lazy?

def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[U] {
  def iterator = coll.iterator map f
}

scala> lazyMap(List(1,2,3,4), (x: Int) => x + 1)
res4: java.lang.Object with Iterable[Int] = (2, 3, 4, 5)
like image 567
platypus Avatar asked Sep 27 '12 23:09

platypus


People also ask

Can You iterate over a map in Scala?

Once you do that you should be able to iterate over the map, print the map elements, etc. In summary, I hope these examples of iterating a Scala Map have been helpful.

What is a map in Scala?

In Scala, Map is a data structure used to map from keys to values. The Scala Standard Library has implemented the Map structure on both immutable and mutable collections for multiple use cases. Immutable maps are the default and are more commonly used. In this tutorial, we’ll look at how Maps work to understand how to iterate over them.

What is the return type of the iterator method?

The iterator method is utilized to give an iterator. Return Type: It returns a non-empty iterator for non-empty map and returns an empty iterator for empty map. Writing code in comment?

What is the use of iterator method in Java?

The iterator method is utilized to give an iterator. Return Type: It returns a non-empty iterator for non-empty map and returns an empty iterator for empty map.


2 Answers

Cause map on iterator is lazy and you need some strictness:

scala> List(1,2,3,4).iterator.map((x: Int) => println(x))
res0: Iterator[Unit] = non-empty iterator

// nothing actually happened yet, just remember to do this printing things

scala> res0.toList
1
2
3
4
res1: List[Unit] = List((), (), (), ())

When you doing foreach on iterator it is quite obvious that you're doing side effects, so lazyness will be undesired. I wouldn't said so about map.

UPD

As for your edit: the reason for such behaviour, is that there is implicit call of toString for statement result which in turn stricts the iterator -- try this code on your own:

scala> { lazyMap(List(1,2,3,4), {(x: Int) => println(x); x + 1}); 1 }

and you'll see that function f is never called

like image 127
om-nom-nom Avatar answered Sep 20 '22 16:09

om-nom-nom


The point of an Iterator is laziness. In other words, when you create an Iterator, it will not evaluate anything until you go to read the data. Here's what that looks like:

scala> val itr = List(1,2,3).iterator
itr: Iterator[Int] = non-empty iterator

Ok, we've got an iterator now. But it hasn't actually looked at the list yet.

scala> val mappedItr = itr.map((x: Int) => println(x))
mappedItr: Iterator[Unit] = non-empty iterator

Now we have a new Iterator. This one will, when data is accessed, apply the function that has been mapped. But we still haven't actually looked at the original list.

scala> mappedItr.next
1

This is the first time we have accessed data, so it is the first time that the Iterator has looked into the list. We called next, so we got the first element. Since our iterator has a map queued up, it applies the mapped function when we access that element. So we see the result of the function applied to the next item.

We can do it again to get the next element:

scala> mappedItr.next
2

And, again, it evaluates the function only when it needs to, in order to give us the final result.

like image 37
dhg Avatar answered Sep 23 '22 16:09

dhg