Range Over Channels in Scala

In a previous example, we saw how for and foreach provide iteration over basic data structures. We can also use similar syntax to iterate over values received from a channel.

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Await}
import scala.concurrent.duration._

object RangeOverChannels {
  def main(args: Array[String]): Unit = {
    // We'll iterate over 2 values in the `queue` channel.
    val queue = new java.util.concurrent.LinkedBlockingQueue[String]()
    queue.put("one")
    queue.put("two")

    // This `Future` simulates the behavior of ranging over a channel
    val future = Future {
      Iterator.continually(queue.poll(100, java.util.concurrent.TimeUnit.MILLISECONDS))
        .takeWhile(_ != null)
        .foreach(println)
    }

    // Wait for the Future to complete
    Await.result(future, 5.seconds)
  }
}

To run the program:

$ scala RangeOverChannels.scala
one
two

In this Scala example, we’ve simulated the behavior of Go’s channel range using a LinkedBlockingQueue and a Future. The Iterator.continually with takeWhile mimics the channel’s behavior of continuing until it’s closed.

This example also shows that it’s possible to add elements to a queue and still process them even after we’ve stopped adding new elements. The poll method with a timeout allows us to simulate the behavior of a closed channel in Go.

Note that Scala doesn’t have built-in channels like Go, so we’ve used Java’s concurrent utilities to achieve similar functionality. In real-world scenarios, you might want to use Scala’s Akka library for more robust and idiomatic concurrent programming.