Scala传感器和Clojure传感器之间有什么相似之处和不同之处?

haw*_*eye 8 scala clojure transducer transducer-machines

Paul ChiusanoRúnarÓli 在Scala编写了一本精彩的函数编程书.在其中他们提到了Scala社区中一个有点引用的概念 - Transducers.

Scala中的Scala传感器

在Clojure的社区- 传感器得到一点 压机.

我的问题是:Scala Transducers**之间有什么相同点和不同点(来自Scala中的函数编程一书)和Clojure Transducers?**

假设:

我知道

  1. 传感器与他们在电气工程中的概念相同

  2. 计算机科学中存在一种称为有限状态传感器的预先存在的概念

  3. 生物学和心理学中有一个先例采用了" 转换"这个词

  4. 目前已经是历史上其他技术书籍像SICP 采用字转换器.

Ste*_*haw 9

来自Scala函数编程(FPiS)和Clojure传感器的流传感器非常相似.它们是使用"机器"(步骤函数)将输入流处理成输出流的概念的概括.FPiS的传感器称为Processes.Rich Hickey 在他关于Clojure传感器的介绍性演讲中也使用了术语过程.

起源

FPiS传感器的设计基于Mealy机器.据说Mealy机器有:

transition function T : (S, I) -> S
output function     G : (S, I) -> O
Run Code Online (Sandbox Code Playgroud)

这些功能可以融合在一起形成:

step: (S, I) -> (S, O)
Run Code Online (Sandbox Code Playgroud)

接下来很容易看出,步进功能对机器的当前状态和下一个输入项进行操作,以产生机器和输出项的下一个状态.

FPiS的组合器之一使用这样的阶梯函数:

trait Process[I, O] {
  ...
  def loop[S, I, O](z: S)(f: (I,S) => (O,S)): Process[I, O]
  ...
}
Run Code Online (Sandbox Code Playgroud)

这个loop功能基本上是Rickey 在本幻灯片中谈到的种子左侧缩减.

上下文不可知

两者都可以在许多不同的上下文中使用(例如列表,流,通道等).

在FPiS传感器中,过程类型是:

trait Process[I, O]
Run Code Online (Sandbox Code Playgroud)

所有它知道的是它的输入元素和它的输出元素.

在Clojure中,这是一个类似的故事.希基称之为"完全脱钩".

组成

两种类型的换能器都可以组成.

FPiS使用"管道"操作符

map(labelHeavy) |> filter(_.nonFood)
Run Code Online (Sandbox Code Playgroud)

Clojure使用 comp

(comp
  (filtering non-food?)
  (mapping label-heavy))
Run Code Online (Sandbox Code Playgroud)

表示

在Clojure中:

reducer:    (whatever, input) -> whatever
transducer: reducer -> reducer
Run Code Online (Sandbox Code Playgroud)

在FPiS中:

// The main type is
trait Process[I, O]

// Many combinators have the type
Process[I, O] ? Process[I, O]
Run Code Online (Sandbox Code Playgroud)

然而,FPiS的代表不仅仅是一个功能.它是一个案例类(代数数据类型),有3种变体:Await,Emit和Halt.

case class Await[I,O](recv: Option[I] => Process[I,O])
case class Emit[I,O](head: O, tail: Process[I,O]
case class Halt[I,O]() extends Process[I,O]
Run Code Online (Sandbox Code Playgroud)
  • Await扮演Clojure的reducer-> reducer函数的一部分.
  • Halt扮演reducedClojure 的角色.
  • Emit代替在Clojure中调用下一步功能.

提前终止

两者都支持提前终止.Clojure使用一个reduced可以通过reduced?谓词测试的特殊值来完成它.

FPiS使用更静态类型的方法,进程可以处于以下3种状态之一:Await,Emit或Halt.当"步进函数"返回状态Halt的过程时,处理函数知道停止.

效率

在某些方面,他们又是相似的.两种类型的传感器都是需求驱动的,不会产生中间集合.但是,我想象FPiS的换能器在流水线/组合时效率不高,因为内部表示不仅仅是"只是一堆函数调用",正如Hickey所说的那样.我只是在这里猜测效率/性能.

考虑fs2(先前scalaz-stream)针对基于在FPiS换能器的设计也许更高性能库.

以下是filter两种实现中的示例:

Clojure,来自Hickey的谈话幻灯片:

(defn filter
  ([pred]
    (fn [rf]
      (fn
        ([] (rf))
        ([result] (rf result))
        ([result input]
          (if (prod input)
            (rf result input)
            result)))))
  ([pred coll]
    (sequence (filter red) coll)))
Run Code Online (Sandbox Code Playgroud)

在FPiS中,这是实现它的一种方法:

def filter[I](f: I ? Boolean): Process[I, I] =
  await(i ? if (f(i)) emit(i, filter(f))
            else filter(f))
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,它filter是由其他组合器构建的,例如awaitemit.

安全

在实施Clojure传感器时,有许多地方需要小心.这似乎是一种有利于提高效率的设计权衡.然而,这种下行似乎主要影响图书馆的生产者,而不是最终用户/消费者.

  • 如果换能器reduced从嵌套步进调用中获取值,则它必须永远不会再使用输入调用该步骤函数.
  • 需要状态的传感器必须创建独特的状态,并且可能没有别名.
  • 所有步骤函数必须具有不接受输入的arity-1变体.
  • 传感器的完成操作必须调用其嵌套的完成操作,只需调用一次,然后返回它返回的内容.

FPiS的传感器设计有利于正确性和易用性.管道组成和flatMap操作确保完成操作立即发生并且错误得到适当处理.这些问题不是传感器实现者的负担.也就是说,我想这个库可能没有Clojure那么高效.

摘要

Clojure和FPiS传感器都具有:

  • 类似的起源
  • 能够在不同的上下文中使用(列表,流,通道,文件/网络io,数据库结果)
  • 需求驱动/提前终止
  • 完成/完成(资源安全)
  • 好吃的 :)

它们的基本表现略有不同.Clojure型换能器似乎有利于效率,而FPiS换能器有利于正确性和组合性.