Input and Output

说明

ReactiveCocoa是iOS里面一个函数响应式编程的框架,在自己的项目里经常使用,对这个框架特别感兴趣,也一直想做些记录,所以现在就开始了。现在这个框架已经是4.0版本了,完全支持Swift,源码看起来很漂亮,但是跟之前的版本有了很大的变化。

接下来的一些博文就是讲解一些基础的概念或者文档,本篇是讲解FRP的一篇译文。

原文地址:Input and Output

正文

Input and Output

应用程序接受输入然后产生输出。这里的输出就是使用输入进行一些加工后的结果。输入,转换,输出,完成。

当应用程序是UNIX工具的时候这种模式就很明显。接收一个字符串,统计单词,输出结果。但是当我们在开发一个有界面UI、大量不同功能、不定时的任务的iOS应用程序时就很难看出这种模式。

在iOS应用程序中什么是输入,什么是输出?

输入就是对应用程序的所有的动作。触摸、键盘事件。定时器触发,GPS定位事件,网络请求响应。这些事情都是输入。这些事情都出现在应用程序中,然后应用程序结合这些所有的输入以某种方式得到一个结果:输出。

在应用程序中输出通常是界面UI的改变。一个开关的拨动或者一个列表获得了新的行。或者更多,比如文件写入磁盘,或者API请求。这些事情都是应用程序的输出。

但是不同于经典的输入/输出模式,这些输入和输出不止发生一次。不是单一的输入->处理->输出,只要应用程序在运行就会继续循环这个过程。实际上应用程序一直都在消费这些输入,然后基于这些输入产出输出。

换句话说,输出就是在那个时间点结合所有输入产生的结果。输出就是在那个时间点所有输入的一个函数。

那么我们为什么要关心这些?因为新的视角具有价值,也是必要的。在当前这种情形中,给了我们一个新的极好的工具。

STATE

从现在这个视角来看,没有任何关于状态的想法在里面。仅仅是输入的改变导向一个新的输出。状态也许是应用程序处理输入的一个实现细节,但是不是必须的。这并不是我们思想的本质。

大多数问题最佳的解决方案都有一些内在的状态。状态可以是本质上的。但是我们并不一定要处理状态。我们做任何事情都是处理状态。因为我们对待我们应用程序的所有输入都是不同的事情-触摸事件,网络响应-我们不能用某种有意义的方式让他们结合起来。不能用统一的方法来转换它们。所以我们处理这些所有不同东西的唯一工具就是状态。当我们的唯一工具就是状态的时候,每一个问题看起来好像都是有状态的。

状态并不好。状态将复杂性引入了进来。更糟的是,复杂性的增长远比我们应用程序的代码量增长。我们习惯于不断的向我们的应用程序引入更多的状态。新功能?新状态。新的复杂性。新的Bugs。

但是普天同庆的是基于我们应用程序的输出是持续时间中所有输入的一个函数的新视角给了我们一个新的工具:函数式响应编程(Functional Reactive Programming)。函数式响应编程(FRP)是基于时变函数产生时变值这种思想而建立的编程范式。

TIME

时变值也许听起来很空洞。难道不是状态的另一种说话吗?他们都捕捉到了相同的思想-只要程序在运行就会有东西在改变。但是通过形式化和具体化的时间变化,我们可以确定改变的理由是很安全的。

时变值可以来自于其他的时变值,这些时变值来自于应用程序的输入。所以当传统的状态卸下保证我们应用程序是在我们熟知状态的重担时,FRP让我们依据时变值来定义我们的应用程序并且保证改变是必需的。

以前,状态是离散的,都是独立的。但是时变值就像是适合一个档位的所有的齿轮。当一个齿轮运转时,就带动了所有与它链接的齿轮,然后一起运转,最后整个机制就靠它们自己运行了起来。

FRP太美了。

有很多事情的行为就像是时变值。异步任务就是时变值,只有完成工作才会有值。或者UI元素的值也可以看做是时变值,当用户交互时才会改变。如果我的应用程序运行在手机设备上,设备的GPS定位系统也是时变值,

所有的事情都是时变值,所有的方式。状态,输入和输出。

FUNCTIONAL REACTIVE PROGRAMMING

函数式响应编程来自于函数式编程语言。

Haskell有 number of implementations ,具有不同程度的完整性和可用性。

Racket,一门Lisp-y语言,有 FrTime

对于命令式语言也有大量的实现。来自于微软的900磅大猩猩 Reactive Extensions(Rx) 。是第一个令人信服的将FRP原则带入到命令式语言的例子。

在JVM上Netflix的 re-implemented Rx 。Clojure, Scala, Groovy, and JRuby有 adapters

Javascript还有一些不同的FRP库。微软的 RxJS ,其他的如 Flapjax BacomJS

Elm 是一门类Haskell语言,可以编译为JavaScript。它是我见过的最漂亮,最实用的FRP实现。

最后但并非最不重要,我们将Rx带入了Objective-C: ReactiveCocoa

EXAMPLE

让我们看看FRP在实际中是如何实现的。FRP在函数式语言中很美丽,但是悲哀的是我们并不是时时刻刻都在使用函数式的语言。

假设我们在开发一个iOS的应用。我们使用ReactiveCocoa来实现我们创建账户的视图。用户将会输入姓名,邮箱(输入两次),还有一个创建的按钮给它们点击。

我们依据输入和输出来思考。对于这些视图来说什么是输入?显而易见的是用户会输入他们的信息。然后会点击创建按钮。就是这些了。对于我们的视图来说这些就是所有的输入了。我们的输出会根据这些输入来定义。

将会有大量的用户界面改变的输出。这个创建按钮只有用户在输入了通过验证(两次邮箱相同,用户名合法)的用户名,邮箱之后才可用。

如果我们按照传统的编程来实现,我们需要检测我们的输入,然后根据输入手动改变创建按钮是否可用:

https://gist.github.com/joshaber/35c68f23e935bff4afd5

但是,其实我们真正想做的就是声明表单验证和创建按钮是否可用之间的关系(在视图整个生命周期中)。

我们想用输入来驱动输出,ReactiveCocoa可以让我们这样做;

https://gist.github.com/joshaber/11f13b2d3b58141d085e

现在不管用户名,邮箱改变,我们都会将这些改变的值减少成为一个布尔值来指示这些表单是否通过了验证然后来指示点击按钮是否可用。

注意例子中的最后一行。不同于一次赋值,实际上建立的是一种关系。不只赋值一次,随着时间的变化我们在一直赋值。这个按钮是否可用来自于表单是否通过验证。输出就是输入的函数。

当创建执行时,我们想要禁用掉输入框然后将颜色变为灰色:

https://gist.github.com/joshaber/f59759d1674f82837b8d

我们可以将剩下的输出像这样类似的连接起来。输出就是我们输入的组合或者转换。

FIN

这是一个在命令式语言中实现FRP原则的简短、实用的例子。完整例子代码: RACSignupDemo

函数式响应编程提供了一种方式,这种方式让我们又一次将编程视为简单的输入和输出。我们最小化了状态并且统一了我们应用程序做的事情。所有的就是输入和输出。

总结

翻译的不好,没有信雅达,见谅。
文中很多链接不可用,还是按照原文放在这里了。

以上。

坚持原创技术分享,您的支持将鼓励我继续创作!