16

I'm looking for a FIFO stream in Scala, i.e., something that provides the functionality of

  • immutable.Stream (a stream that can be finite and memorizes the elements that have already been read)
  • mutable.Queue (which allows for added elements to the FIFO)

The stream should be closable and should block access to the next element until the element has been added or the stream has been closed.

Actually I'm a bit surprised that the collection library does not (seem to) include such a data structure, since it is IMO a quite classical one.

My questions:

  • 1) Did I overlook something? Is there already a class providing this functionality?

  • 2) OK, if it's not included in the collection library then it might by just a trivial combination of existing collection classes. However, I tried to find this trivial code but my implementation looks still quite complex for such a simple problem. Is there a simpler solution for such a FifoStream?

    class FifoStream[T] extends Closeable {
    
    val queue = new Queue[Option[T]]
    
    lazy val stream = nextStreamElem
    
    private def nextStreamElem: Stream[T] = next() match {
        case Some(elem) => Stream.cons(elem, nextStreamElem)
        case None       => Stream.empty
    }
    
    /** Returns next element in the queue (may wait for it to be inserted). */
    private def next() = {
        queue.synchronized {
            if (queue.isEmpty) queue.wait()
            queue.dequeue()
        }
    }
    
    /** Adds new elements to this stream. */
    def enqueue(elems: T*) {
        queue.synchronized {
            queue.enqueue(elems.map{Some(_)}: _*)
            queue.notify()
        }
    }
    
    /** Closes this stream. */
    def close() {
        queue.synchronized {
            queue.enqueue(None)
            queue.notify()
        }
    }
    }
    

Paradigmatic's solution (sightly modified)

Thanks for your suggestions. I slightly modified paradigmatic's solution so that toStream returns an immutable stream (allows for repeatable reads) so that it fits my needs. Just for completeness, here is the code:

import collection.JavaConversions._
import java.util.concurrent.{LinkedBlockingQueue, BlockingQueue}

class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] = new LinkedBlockingQueue[Option[A]]() ) {
  lazy val toStream: Stream[A] = queue2stream
  private def queue2stream: Stream[A] = queue take match {
    case Some(a) => Stream cons ( a, queue2stream )
    case None    => Stream empty
  }
  def close() = queue add None
  def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}
Stefan Endrullis
  • 4,150
  • 2
  • 32
  • 45
  • 1
    If the FIFO is mutable, how can it behave like a stream ? You want some kind of journalized structure ? – paradigmatic Sep 26 '11 at 09:54
  • I thought about a FIFO as a combination of an input and an output stream (building a queue). Then the output stream would be nothing else than a Scala stream which is anyway evaluated lazily. But maybe this was never the intention of the Scala developers. The output stream in my `FifoStream` class is also a bit separated and accessed via FifoStream.stream. However, I would actually prefer a single object in which something can be piped and read from. – Stefan Endrullis Sep 27 '11 at 07:29

3 Answers3

16

In Scala, streams are "functional iterators". People expect them to be pure (no side effects) and immutable. In you case, everytime you iterate on the stream you modify the queue (so it's no pure). This can create a lot of misunderstandings, because iterating twice the same stream, will have two different results.

That being said, you should rather use Java BlockingQueues, rather than rolling your own implementation. They are considered well implemented in term of safety and performances. Here is the cleanest code I can think of (using your approach):

import java.util.concurrent.BlockingQueue
import scala.collection.JavaConversions._

class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] ) {
  def toStream: Stream[A] = queue take match {
    case Some(a) => Stream cons ( a, toStream )
    case None => Stream empty
  }
  def close() = queue add None
  def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}

object FIFOStream {
  def apply[A]() = new LinkedBlockingQueue
}
paradigmatic
  • 40,153
  • 18
  • 88
  • 147
  • Thanks. That's what I was looking for. I did not know there is also a BlockingQueue in the JRE. Pretty useful. It gracefully simplifies the code. I slightly modified your solution so that toStream returns a stable stream (allowing for repeatable reads) and added it to the end of my question. Thanks again. – Stefan Endrullis Sep 27 '11 at 12:55
  • 2
    This looks useful! But should it be `def apply[A]() = new FIFOStream[A](new LinkedBlockingQueue)` ? – bbarker Aug 21 '15 at 17:00
2

I'm assuming you're looking for something like java.util.concurrent.BlockingQueue?

Akka has a BoundedBlockingQueue implementation of this interface. There are of course the implementations available in java.util.concurrent.

You might also consider using Akka's actors for whatever it is you are doing. Use Actors to be notified or pushed a new event or message instead of pulling.

drstevens
  • 2,903
  • 1
  • 21
  • 30
  • `BoundedBlockingQueue` is interesting but seems in its functionality similar to existing queues like `scala.actors.threadpool.LinkedBlockingQueue`. Both queues are not closable and do not implement `Traversable` and therefore cannot be used in for comprehensions without importing `JavaConversions._`. Moreover, I noticed that the read operation did not block when I tried to read elements that had not been added before. – Stefan Endrullis Sep 27 '11 at 08:34
0

1) It seems you're looking for a dataflow stream seen in languages like Oz, which supports the producer-consumer pattern. Such a collection is not available in the collections API, but you could always create one yourself.

2) The data flow stream relies on the concept of single-assignment variables (such that they don't have to be initialized upon declaration point and reading them prior to initialization causes blocking):

val x: Int
startThread {
  println(x)
}
println("The other thread waits for the x to be assigned")
x = 1

It would be straightforward to implement such a stream if single-assignment (or dataflow) variables were supported in the language (see the link). Since they are not a part of Scala, you have to use the wait-synchronized-notify pattern just like you did.

Concurrent queues from Java can be used to achieve that as well, as the other user suggested.

axel22
  • 32,045
  • 9
  • 125
  • 137
  • That's an interesting approach for a very concise syntax, but I double it will be added to next Scala releases. – Stefan Endrullis Sep 27 '11 at 08:42
  • It was considered at one point, but it remains unclear if it will. The problem is that its main use-case are dataflow parallel frameworks - it would have to provide some kind of hooks for the data flow parallel framework instead of generically being implemented with a `wait`-`notify`. – axel22 Sep 27 '11 at 08:45
  • DataFlow concurrency is available in Akka, and Akka will be merged with Scala Standard Library: http://akka.io/docs/akka/1.2/scala/dataflow.html – paradigmatic Sep 27 '11 at 09:09
  • Yes, but not as a language construct within the language itself - it's available as a library routine. – axel22 Sep 27 '11 at 09:11
  • @axel22 I don't see the problem ! – paradigmatic Sep 27 '11 at 21:16
  • @paradigmatic - well, there is no problem, but when discussing how to implement such a queue in terms of the primitives offered by the language, then without an Oz-style single-assignment variable, there is no other way than to use `wait`-`notify` :) – axel22 Sep 27 '11 at 22:09