从 Stream 和函数式编程想到的

面向对象的问题

我做为自学的程序员, 绕了不少弯子, 缺了不少编程的基础理论
做为前端我也能拉不少人下水, 由于不少写界面的人也是自学的
编程语言从 Fortran 跟 Lisp 已经被研究了半个世纪, 理论成果也是连篇累牍
咱们经常以为本身已经在编程了, 可是基于什么编程呢?html

首先编程固然是对真实世界的状况的模拟, 这一点做为基础
固然问题关键是, 用什么来模拟?
一类是表达式, 或者说递归嵌套的表达式, 好比 ((x * x) + (y * y))
不过表达式功能有限, 注意到吗, 这里边是没有状态的
那么, 有状态的对象怎么模拟? 面向对象编程(OOP)说, 用面向对象啊前端

其实"对象(Object)"这个词有点滥用翻译了, 什么都是 Object 啊
OOP 当中的对象很是特别, 特指有内部状态的对象
好比鼠标的位置 P, 经过 P.get() 读取, 会根据具体状况改变
面向对象以此为基础, 说, 这能够模拟真实世界, 对象有内部状态
因而编程就是各类对象经过接口交换内部状态, 而最终造成html5

然而函数式编程来讲, 它也有对象, 但并无可变的那种对象
Rich Hichkey 说, 一个值变来变去, 那就是不可靠的
特别是在复杂的系统中, 甚至并行当中, 发生混乱了怎么定位
其实函数式编程也有讲上边的问题, 真实世界各类状态, 怎么模拟?
给了个例子, 好比英国国王是谁? 不清楚! 可是具体哪一年英国国王是谁? 知道了!
值并非任意改变的, 而是随着时间改变的, 就像 f(x) 函数
内存里的数据也不是任意改变的, 而是每一个时刻有一个肯定的状态node

SICP

因此, 在函数式变成当中, 随着时间改变的就不是变量, 而是"流(Stream)"
这一点, 在 SICP 当中关于流的介绍作了这样的说明:
http://sarabander.github.io/sicp/html/3_002e5.xhtml#g_t3_002e5python

Can we avoid identifying time in the computer with time in the modeled world? Must we make the model change with time in order to model phenomena in a changing world?git

Think about the issue in terms of mathematical functions. We can describe the time-varying behavior of a quantity x as a function of time x(t). If we concentrate on x instant by instant, we think of it as a changing quantity. Yet if we concentrate on the entire time history of values, we do not emphasize change — the function itself does not change.程序员

If time is measured in discrete steps, then we can model a time function as a (possibly infinite) sequence. Stream processing lets us model systems that have state without ever using assignment or mutable data.github

刚接触 Lisp 那时常常看到帖子里有人推荐 SICP, 说是很棒的书
里边有大量的习题, 我看不下去, 到最近也只是把文字描述浏览一遍
http://kidneyball.iteye.com/blog/922953 说的彻头彻尾的教程啊web

SICP,Structure and Interpretation of Computer Programs,计算机程序的构造和解释,是美国麻省理工学院(MIT)的计算机科学(CS)与电子工程(EE)本科的一门必修课。这本书在1984年出版,而自从1980年开始,20多年来此书的内容一直是MIT的计算机编程入门课程,而且被世界各地百余所大学效仿。SICP是基于LISP语言展开论述的,直到2008年,才被另外一门基于python语言,但原理相同的课程取代。编程

无论怎样, 真的是一本很棒的书, 即使我很不喜欢看, 也不爱作习题
如今的编程当中遇到的不少问题, 至少理论上很早就被研究过了
SICP 当中程序数据甚至语言自己的抽象, 今天的脚本语言仍然混淆
而我也最近才慢慢学习理解到 Stream 这个概念有多么重要

架构图

先抛开代码, 看看现实当中的大问题咱们怎样思考, 咱们经常画图
图形界面怎样编写, 有 MVC 这样的方案, 拆分出一些大的模块
而后用户有操做, 服务器有数据, 就开始在几个模块之间流动, 像这样:

React 造出来 Flux, 至少看架构图差异不是那么大, 也是几个组成部分
而后用户操做 Action, 以及数据, 沿着一个方向流动, 像这样:

Apple 的 JavaScript 引擎, 用 LLVM 优化编译 JavaScript 代码
整个流程也划分红了一些模块, 而后数据在模块之间逐个传递, 像这样:

对于前端开发者来讲 DOM 和 CSSOM 的解析也是被拆解的过程
二者分别解析, 最后合并成一个用于渲染的 DOM Tree, 最后渲染
整个过程实际上是从网络加载代码, 沿着箭头流动处理的过程, 像这样:

游戏引擎的架构我不熟悉, 不过拆分了模块以后大概的意思也好懂, 像这样:

我想说的是, 解决问题时, 拆分模块, 在之间传递数据, 很通用的办法
编程经常就是大问题拆成小问题, 而后拼在一块儿解决, 也是这意思

FBP(Flow-based Programming)

若是按照上边的思路直接去找相似的编程语言, 那也是有的
FBP 的概念在 1970s 就出来了, 具体说来我也不懂, 本身看 Wiki 吧
https://en.wikipedia.org/wiki/Flow-based_programming
不过前两年 Node 社区有个 Noflo 很火, 看界面也是挺棒的
只要找到须要的模块, 而后连一连线条, 就能把程序写出来了:

Noflo 也许不实用, 可是基于 Quartz Composer 的 Origami 总能用了
这个软件里经过拖拽就能建立出能够交互的图形界面
第一次看同事演示的时候, 尽管知道 Noflo, 仍是以为这很高明很强大
并且也说明, 复杂程序分明是能够不写代码, 直接拖着就出来了的:

而后来回顾一下, 咱们天天写的代码, A 状态改变, B 怎么跟着改变?
A 发生了什么, 我作事件监听, 而后对 B 进行操做, B 终于跟着改变了
但是用上边的图形呢, 拖一条线, 把 B 跟 A 关联起来, 不就行了吗

Streams 和 Monad

晚上我翻到一片文章, 讲的是 Swift 当中的 Monad
我知道这文章就是从 Haskell 改写的, 并且图片还很是好玩
http://www.mokacoding.com/blog/functor-applicative-monads-in-pictures/



Stream 在 Haskell 里大概也是 Monad 实现的, 细节我有点模糊
反正 Haskell 里的 State 都用 Haskell 封装隔离, 也不会差太多了
Monad 颇有意思, 被比做一个盒子, 盒子里装了数据, 甚至还装了函数
而后 Monad 讲的都是数据甚至函数包在盒子里怎么去操做的故事
过程不难懂, 但有个事情很费解, 好端端地干吗把数据装进盒子里?
我写 CoffeeScript 那么久了, 直接操做数据多方便, 为何要盒子包起来?

到这里! 我要跳跃了, 回想一下前面说的 Stream, 还有架构图, 数据在哪?
Stream 是随着时间改变的数据, 是一个流, 不是单个单个的数据, 有盒子对吧
架构图上, 数据在模块里被处理, 沿着箭头被模块发送到另外一个模块,
然而, 注意编程语言怎样发送数据, O.emit(data) 吗, 可是内部实现是什么?
再想一下, 是应该有一个管道, 数据被发送到管道里去了, 也像是盒子对吧
模拟状态的时候, 经常有一层封装, 就像是盒子, 或者说管道, 好比说 Stream

并且若是把 Stream 架构图上的箭头用 Stream 来替换的话, 就更清晰了
一般调用函数获取数据, 在图上至关于从后边调用前边模块的数据
这个过程看起来流程后面的组件在驱动, 而不是前面的模块在驱动
可是 Stream 的话, 固然是前面的模块执行完, 再流动到后面的模块
加入流的概念以后, 架构图的方向和思路就清晰多了
我这样说, 是由于看过 Elm 和 PureScript 当中 Signal 实现 Flux 的例子
相比 JavaScript 当中 Flux 用 Dispatcher, Signal 的写法清晰太多了

咱们想要模拟现实世界, 然而面向对象声称的办法依然不够强大
可变的状态, 状态一改变就消失了, 再也找不回来
监听和操做数据, 写起代码来倒是各类繁琐, 依然须要继续作抽象
我想到 Node.js 的 Stream, JavaScript 是 OOP, 然而实现了 Stream
Stream 能够是基于 EventEmitter 的封装, 把数据包在事件流当中的
而 Stream 带来什么样的方便呢, 组合起来很很灵活

流仍是很难

前面罗列那么多, 并非说我理解了, 而是说终于我把一些困惑串在一块儿了
我知道了为何会有 Stream, 为何 Stream 很重要, 我应该往哪儿学习
Node.js 当中实现的流相对简单, 前端事件流也还不错
可是也有复杂的, 敢不敢看看 PureScript 当中 UI 操做的 Signal 是如何处理的
http://begriffs.com/posts/2015-07-10-design-of-purescript-halogen.html
Signal 至关于流的概念, 但在 ADT 类型当中真是太难理解了

另外 Go 的 Channel 彷佛也是流, 虽然有了新的名字, 新的操做语法 还有 Java 之类我没法理解的语言, 其中固然也实现了 Stream, 但我不懂 不懂因此文章写到这里就结尾吧...

相关文章
相关标签/搜索