使用类型类建模生产者 - 消费者语义?

时间:2014-08-16 21:24:55

标签: scala functional-programming typeclass producer-consumer

如果系统中的某些实体可以充当数据或事件的生产者,而其他实体可以充当消费者,那么将这些“正交关注点”外部化到生产者和消费者类型类中是否有意义?

我可以看到Haskell管道库使用这种方法,并且欣赏这个问题对于来自Haskell背景的人来说可能看起来非常基本,但是会对Scala视角和示例感兴趣,因为我看不到很多。

1 个答案:

答案 0 :(得分:2)

你应该看看Matt Might的this article

它为您提供了ProducerConsumerTransducer(您提到的haskell库中的Pipe)的简单实现,以及如何使用它们创建Web服务器的示例。

基本上每个Producer扩展Runnable并有一个私有缓冲区来输出元素。缓冲区是java ArrayBlockingQueue,它是线程安全的。

每个Consumer也是Runnable,并且具有使用类似架构的输入缓冲区。

当您将Consumer链接到Producer时,您会创建另一个Runnable。 一开始,它将启动ProducerConsumer(它们是Runnable)并将在它们之间传输数据。

当您将Transducer链接到Producer时,会创建一个新的Producer

因此,如果您遵循他的实现,您应该能够以haskell的方式编写:

listen ==> connect ==> process ==> reply

以下是从上面的链接中复制和改进的一些代码:

import java.util.concurrent.ArrayBlockingQueue

trait Coroutine extends Runnable {
    def start() {
        val myThread = new Thread(this)
        myThread.start()
    }
}

trait Producer[O] extends Coroutine {
     private val outputs = new ArrayBlockingQueue[O](1024)
     protected def put(output: O): Unit = outputs.put(output)
     def next(): O = outputs.take()

     def ==>[I >: O](consumer: Consumer[I]): Coroutine = {
         val that = this
         new Coroutine {
             def run() {
                 while (true) { val o = that.next(); consumer.accept(o) }
             }

             override def start() {
                 that.start()
                 consumer.start()
                 super.start()
             }
         }
     }
}

trait Consumer[I] extends Coroutine {
    private val inputs = new ArrayBlockingQueue[I] (1024)
    def accept(input : I): Unit = inputs.put(input)
    protected def get(): I = inputs.take()
}

以下是您可以使用它的方法:

case class IntProducer(zero: Int) extends Producer[Int]{
    def run(): Unit = {
         var i = zero
         while(true) { put(i); i += 1 }
    }
}

object Printer extends Consumer[Any]{
    def run(): Unit = {
         while(true) { println(get()) }
    }
}

val pip = IntProducer(0) ==> Printer
pip.start()

要查看更多示例以及如何处理`传感器,请查看my Gist