從 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, 但我不懂 不懂因此文章寫到這裏就結尾吧...

相關文章
相關標籤/搜索