我剛寫順手 CoffeeScript 的時候對程序的理解固然不同,
coffee 當中思路還算清晰, 全局變量和局部變量, 而後有函數,
從而造成大大小小的對象以及閉包, 而後之間的數據發生相互做用,
而這些關聯和互做用足夠複雜, 能夠模擬咱們業務所需的邏輯,
做爲腳本語言來講, 很是靈活的一套方法了.前端
雖然 JavaScript 自己花樣挺多, 但 coffee 裁剪的核心很是小,
能夠看作是個 good parts 的精簡版. 而這些並不足夠,
後來 ES6 不斷增長功能, 這個事情你們也看到了, 編程語言會很複雜.
而這期間我開始深刻挖 Clojure 方向的技術, 特別是 ClojureScript,
從 React 方向上走, cljs 是很是深思熟慮的語言, 也很天然而然的.數據庫
就我而言, cljs 對個人思惟方式產生了巨大的影響.
並且隨着我嘗試去接觸前端以外的一些內容, 想法也也在改變着,
對於不少人來講, 我如今反思的內容, 也許未曾被疑問過,
對於我本身來講, 這些思惟上的轉變很是重要, 影響個人思考和工做.
固然整理出來, 也更能明確表示我對於 js 和 cljs 的態度.express
使用 coffee 的時期, 一切皆是表達式的觀念已經根植在腦海裏了,
因爲都是表達式, 代碼的可組合性很是高, 幾乎是任意組合,
好比說 if
在 js 裏是 statement, 在 cljs 中是 expression,
那麼 cljs 中 if
能夠用在代碼的任何位置做爲參數使用,
就像是 HTML 當中, <div>
的結構能夠很是靈活地組合使用.編程
我原覺得 coffee 的表達式已經足夠靈活, 但 cljs 還賽過 coffee,
固然這是 Lisp 風格語法的緣由, 全部基於 S 表達式的語言都能作到,
其實在 coffee 當中還有不少縮進的顧慮, 組合會語法麻煩,
而在 js 的 C 風格語法, 甚至在 ts 和 flow 中, 這些問題會更明顯,
語言自己的語法制約了其靈活性, 雖然不影響強大, 但總歸囉嗦了不少.
我如今以爲 S 表達式對於組合能力是至關重要的提高.數據結構
我對 Java 語言不熟悉, 而 js 的面向對象又是花樣百出的,
固然我理解的 OOP 顯然是有偏頗的, 想法並不許確.
我更願意接納 Alan Kay 說的那種基於消息傳遞的理解,
假設有不少的細胞各自工做, 之間經過信號來協調狀態,
這也是整個互聯網巨大的生態所展現的形態, 大量的聯網的機器,
機器之間經過收發消息來溝通, 從而造成巨大的程序集合體.閉包
然而具體到編程語言當中實現這樣的模型, 好比 js, 獲就很怪異,
首先代碼是單線程執行的, 編程模型其實仍是單線程,
OOP 在 js 中只是將代碼進一步結構化了, 算是好維護一點.
而後因爲對象實例是 js Object, 能夠用 js 代碼隨意操做,
結果就是對方拿到某個引用, 就能任意修改數據, 這就邪門了.
若是別人的計算機能直接修改你計算機上的數據, 不是亂套了嗎.併發
我以爲這是編程語言具體實現而帶來的錯覺, 這不屬於 OOP,
OOP 能夠幫助分隔職能以便於代碼能更好地組織,
可是不必搞成對弈共享內存的不可靠的程度.
固然這可能只在 js 社區早一點的時候比較嚴重, 如今並不清楚.
當 Clojure 社區批評面向對象是 place oriented Programming,
可能就是批評錯了, 那些概念真的屬於 OOP 嗎, 我很懷疑.異步
其實收發消息的模型更像是併發編程, 而不是單線程,
當你有大量的 goroutine 獨立作本身的功能, 這種模式就清晰起來,
每個輕量級進程有本身的內部狀態, 而後收發消息:async
Do not communicate by sharing memory;
instead, share memory by communicating.編程語言
這樣也就避開了直接拿到引用去別改別人的數據的問題.
並且也更天然, 就像 HTTP 服務發送的字符串數據同樣.
消息就是不可變的, 若是不同, 那就是一份新的數據了.
而這樣的機制也保證了巨大的互聯網可以正常地運轉.
說到併發編程, 我大體以爲應該分紅兩種, 好比兩個進程之間,
一種方式是兩個進程相互有依賴, 要等到對方的行爲,
另外一種方式是二者基本上無關, 一塊兒啓動就行了.
第一種, 也就是進程之間相關依賴的狀況, 是我很關心的,
而 Go 的 CSP 模型, 當中的 channel, 就致力於解決這類問題.
有多個進程, 他們之間須要相互協做, 經常要等待, 那就用管道.
那我認爲的編程語言兩個功能, 一個是模擬, 一個是計算,
真實的物理世界, 或者說具體的業務, 有巨大的複雜性,
當你要用編程語言解決問題, 首先語言應該有足夠的靈活性去描述問題,
而後是計算, 比說你能描述字符串文件, 也能描述 zip 文件,
那兩種形態之間的轉化過程, 語言就要能進行拆解由 CPU 完成計算.
更重要的例子固然是多個任務之間相互協做, 須要能模擬和計算.
純數據固然足夠明確了, 還有討論一下可變狀態和時間的問題,
因爲內存和磁盤是可變的, 其實編程語言內建就有可變狀態,
只是說從 Clojure 和 Haskell 的角度, 直接這麼作是容易失控的,
因此抽象出了 reference 的概念, 值不能夠修改, 但能夠修改引用.
時間指的是異步任務的等待, 或者是事件流這種狀況.
我以爲是說, 編程語言應該給出對應的明確的抽象, 來講明它們是什麼?
而後纔有清晰的方案說遇到這種狀況怎麼處理.
js 做爲腳本語言而生, Java 和 C# 已解決的問題它卻沒有解決.
而如今 js 又忙不迭地要加上這些那些功能..
這種作法在 Clojure 看來真的是太混亂了, 想到什麼加什麼.
我不以爲修補問題是壞事, 只是說很難避免不少次生的問題,
好比說社區大量風格不一致的類庫, 難以輕鬆使用.
本來但願語言自己作好模擬和計算, 結果光是模擬就費好大的勁.
因爲 Flux 的緣由, 咱們前端開始關心 Single Source of Truth,
數據是怎麼來的, 最原始的形態是怎樣, 如何分割?
若是你拿到一個數據 1
, 你固然也能夠說這就是 1
, 毫無疑問,
但若是是一個不斷變化的數字, 或者說 React 應用的 Store, 就不簡單了,
這個 Store 當前的內容並不是 Source, 而是一個結果,
是一個出事狀態和每一個後續的操做, 最終造成的結果,
就像是如今的 Git 倉庫, 是從空倉庫加上所有的 commits 才獲得的.
這個問題到了數據庫, 以及作備份和同步策略的時候更加明顯,
而每一個原子性的操做成了 Source, 纔是最真實最小的單元.
可變狀態是什麼? 就是這些原子性的操做進行計算的中間狀態,
於是當面對一個數據庫的數據時, 數據庫能夠認爲是可變狀態,
而實際上數據庫是這些原子性操做的集合, 而且隨着時間改變.
回到程序當中的局部變量, 實質上也是這樣, 一些操做在時間上的集合.
基於這樣的角度, 時間這種外部條件變化, 程序的可靠性就存疑了.
那麼說到時間, js 社區近些年纔開始集中精力去解決,
好比說開頭的 Promise, 而後是 Generator 和 async 函數, 以及 RxJS.
而我會說 CSP, 也就是 Go 的 channel, 就是前幾篇文章的內容.
咱們要模擬這個世界當中的具體業務, 離不開時間因素,
所以對於時間相關的代碼的抽象也就成了至關有份量的工做.
那我就以爲, 能給出基礎的操做時間的方案, 那應該是最可行的.
好比 CSP 當中有 timeout
, 在 put!
和 take!
過程都有 wait 機制,
經過這些函數, 或者說指令, 時間成了編程語言執行的一部分,
js 須要用回調解決掉問題, 這裏顯得比較清晰了, 也就是等待.
還有 alts!
之類的更強大的機制, 能作更多的控制.
也能夠說 Future, Promise, async, Reactive Programming, 也是辦法,
那個人意思就是我認爲 CSP 是其中最爲明確和天然的方案.
Rx 固然很強大, 但那是類庫, 像是黑盒, 而不是單純語言提供的抽象.
C 風格的語言每每從硬件角度, 提供 mutable 的數據結構,
而 Clojure 跟 Haskell 當中, 這一點是很是謹慎的,
Clojure 認爲數據就是數據, 怎麼能隨意更改數據, 只能更改引用,
對應到真實世界, 蘋果就是蘋果, 怎麼可能變成梨?
只會發生的是, 盒子裏原來放蘋果, 如今放梨, 關係隨着時間改變了.
因此這纔是更準確的用代碼模擬世界的方式, 蘋果不能變梨.
一樣地, 當代碼複雜到跨越大量的機器, 在不一樣的時間節交換狀態,事情自己是會愈來愈複雜的, 無法避免, 編程語言仍是要模擬和運算,但是咱們有機會找到更準確的概念去描述他們, 並且更準確,單純的腳本語言表達能力固然不夠, 結果就須要不斷增長新的概念,好比說造一個些類庫和語法, 加一些新的概念, 說能解決這個問題,問題在於, 這些概念自己也可能過於複雜, 超出文本自己的複雜.這種時候某些語言表達能力足夠強, 把問題弄透徹了, 那就讚了.