當前位置:
首頁 > 知識 > 程序員們,是時候面向故事編程了!

程序員們,是時候面向故事編程了!

點擊上方「CSDN」,選擇關注

關鍵時刻,第一時間送達!

作者 | Brandon Keown

程序員們,是時候面向故事編程了!

【CSDN編者按】其實,本文並不是說讓你將系統設計成故事一樣,而是利用故事的心理技巧來發布設計決策和討論。像講故事一樣設計系統的好處在於,可以讓開發者將設計決策分發給那些能修辭敘述的人,而不必非得局限於某個領域,如高階數學。

以下為譯文:


精靈的世界

Handle 是一個小精靈。Handle 可以創造其他精靈。Handle 會按照其喜好的樣子創造精靈,而他的想像總是成對出現。今天 Handle 創造了兩個孩子 Reqla 和 Resnak。但他不知道為什麼要創造這些孩子。只是為了存在而創造他們。繆斯神 Polys 啟發了 Handle,於是 Handle 用自己的創造力詮釋了靈感。創造完孩子以後,Handle 將他們送到了精靈學校,學校的精靈老師 Routin 負責幫助他們尋找存在的意義。

Routin 馬上明白了為什麼 Resnak 很重要。他發現,Resnak 存在的意義就是將 Reqla 的意義傳回給 Polys。每當有人告訴 Resnak 一些事情,他和 Reqla 就會融入以太。很重要的是,與 Resnak 交談的人應該用最佳方式與他交流,因為 Resnak 可以直接與 Polys 交談,而且他能夠辨別 Reqla 的價值,並告訴 Polys,從而讓 Polys 感受到自己的創作為人理解的喜悅。但他並不了解 Reqla 的目的。他與 Reqla 交談,試圖找出她如何才能最好地為 Polys 服務。

一般來說,當 Routin 在與精靈孩子打交道的時候,如果他能確定他們的目的,那麼他會派出這些孩子來完成目的。否則,他會讓能與 Polys 交談的孩子(這裡是 Resnak)去告訴 Polys,他不知道另一個孩子是來幹什麼的。

今天,Routin 發現 Polys 正在自己玩一種語言遊戲。由於 Polys 對自己每天用的語言感到了厭倦,所以他希望聽到其他語言。然而,Routin 不懂語言,不過他知道有人懂。於是他把 Reqla 和 Resnak 送到了唯一的一家精靈翻譯公司。

當他們到那裡的時候,他們遇到了公司的領班 Logon。Logon 掌握的語言程度剛好可以讓他找出公司里最合適的人選。與 Reqla 進行了簡短的交談後,Logon 知道 Reqla 應該與誰交談,才能翻譯她帶來的消息。

讓我們暫停一下,多一點互動。

現在 Reqla 和 Resnak 與 Logon 在一起。Logon 知道有一個翻譯員能夠翻譯 Reqla 的消息。接下來會發生什麼?Reqla 和 Resnak 都應該去找那個翻譯員嗎?如果都去了,是應該由翻譯員跟他們打招呼,還是 Reqla 應該跟翻譯員打招呼呢?翻譯完了以後,是應該由翻譯員告訴 Resnak 翻譯的結果,還是應該讓 Reqla 告訴他呢?Resnak 是不是應該留在 Logon 那裡?如果他留下,那麼誰把翻譯結果帶回來呢?翻譯員應該留在原地不動嗎(也許他很胖,無法輕易挪動)?回來以後,捎信的人是應該直接告訴 Resnak,還是先告訴 Logon,然後由 Logon 告訴 Resnak?先告訴領班有意義嗎?領班需要現在內部(或者與他外部的會計同事)交涉一下,再告訴 Resnak 嗎?退一步說,或許讓翻譯人員直接面對另外三個人可能更加有效率。

無論你選擇回答哪個問題,最終都會導向同一個問題:什麼樣的推理在邏輯上與人為的世界一致。如果你發現翻譯人員都被關在象牙塔里,而你只能沖他們大喊,他們才會回答你,那麼你就無法把他們叫到領班的辦公室。

如果你有 web API 開發的背景,你可能會意識到上面我們講述的是一個 Web 翻譯服務稍稍擬人化的故事。用典型的行業術語來說就是:請求和響應由底層的 http 伺服器成對生成。處理程序將請求/響應分發到路由,由它來決定調用哪個子處理程序。如果是翻譯請求,那麼語言子處理程序則根據語言檢測的啟發,將請求分發到翻譯服務上,該服務可能不在同一個伺服器上,但是可以通過子請求遠程訪問。翻譯完成以後,通過響應體將翻譯結果返回給請求者。

顯然,用專業術語描述該服務要比故事簡短得多。而且,短小精悍是一種非常誘人的優點。

但對於實現需求而言,這種簡短是否會造成模稜兩可或晦澀難懂呢?

當我向另一個開發人員描述該服務的時候(如果他們問及),肯定會採用第二種專業術語的方式。我當然不會像上面那樣編造一個故事。(我花了至少 30 分鐘才確保了故事敘述的一致性,且合乎大眾讀者的口味,雖然我仍然不確定是否合乎大眾的口味,哈哈。)

但是,在我試圖將上面的故事變得更詳盡,同時強迫自己將各個組件分解成活躍的子進程(即精靈)並讓他們的行為更像人類的過程中,我發現這裡引發的橫向思考往往被「正規」系統「設計」所忽略。


敘事推理

人類的直覺推理似乎與敘事的概念緊密相關,特別是涉及其他人類角色的敘事。儘管人類能夠推理一些看似與其他人類沒有明確關係的事物,如等邊三角形等正幾何體,但這種行為貌似僅限於鍾型曲線的右側。(註:這裡鍾型曲線指人類的智商分布呈正太分布,「鍾型曲線的右側」指高智商的那些人。見《鍾型曲線:美國社會中的智力與階層結構》,理查德?赫恩斯坦、查爾斯?默里,1994 年 9 月)

正常人類都有敘事推理的能力。這是最普通的人類的方式。在這無數的推理中,可能隱藏著一個突發問題的解決方案,即便是一個平常的人也可以解決。這類似於支持向量機(相當於一個非常聰明的人)等非常強力的分類器與鏈式決策樹樁(相當於一群普通人)等可調節的增強版的弱分類器之間的區別,在機器學習中,水平一般的人由於其平庸無法貢獻驚人的解決方案,但他可以向問題施加適當的壓力從而得到解決方案。這反而讓我想起了人民的智慧是無限的。有些人比所有人都聰明。但是現實中這樣的人非常罕見,而且有趣的是聰明人無法與其他聰明人或不聰明的人很好地相處。

如果我們想對複雜事物(例如系統等)的推理進行民主化,特別是在推理引發這種複雜性的事物間相互作用時,一個似乎很明智的做法是將這些組件當作人類代理或意向載體,因為使用這種方法,普通人也可以進行推理。太多的情況下,我們會在推理過程中認為,人的意圖是由於系統的組成部分而產生的。那麼,不要將專業術語和行為類比混在一起使用,而是將組件當成小人,或小精靈,小旅鼠等等,會怎麼樣呢?如果我們不用高科技術語來發表關於系統開發的演講,我們只是講故事,會怎麼樣呢?


道德論點

我們的世界中存在必要的道德,軟體也一樣。當今世界受到軟體的驅動。其中大部分都寫得很糟糕,很難對其進行推理,但是很神奇的是 80% 的時間裡它們依然能夠正常工作。而 bug 肆意猖獗,即便在已解決的問題領域中,bug 也是層出不窮。很多時候,這些 bug 並不是由於推理單個組件運作的困難性,而是由於推理組件之間的關係和溝通模式的困難性。將軟體分解成組件是消滅 bug 的良好開端,成千上萬的文章都揭示了其中的緣由。

我們需要編寫可靠的軟體。人們的生活常常依賴軟體,包括我自己。為了履行這最基本的道德標準,我們必須認識到目前關於系統的推理方法是不充分的。


解構歷史

歷史上出現過好幾種構建系統的重要方法,試圖增強系統的可推理性。一些例子如結構化編程,九十年代的面向對象編程,以及最近又興起的函數式編程。然後,橫跨所有這些方法的還有靜態類型,試圖在某些方面進行自動推理,以減輕人類的繁重工作。

九十年代之前被廣泛採用的是結構化編程,即依照一些啟發式原則,將過程分解成子過程,以保證過程的正確行為。但子過程可以做任何事,它們可以通過不可預測的方式操作其他過程依賴的狀態,等等。維基百科說:


結構化編程是一種編程模式,它廣泛使用結構化控制流程,如選擇、重複、塊解構、子常式,試圖增強計算機程序的清晰程度、質量並減少開發時間。結構化編程避免使用簡單條件測試和 goto 等跳轉語句,因為這些會導致「麵條式代碼」,增加閱讀和維護的難度。

九十年代流行的分解問題的方式,現在依然被稱為「面向對象編程」。不幸的是,Alan Kay 博士早在七十年代發明了面向對象的概念,其實與當下流行的面向對象並不相同,卻完全被人遺忘了。這種面向對象編程的主要問題是,事物擬人化經常會造成扭曲的設計,如「電梯對象詢問樓層對象以得知是否需要停止運行」。什麼意思?樓層又不會說話,電梯也不會說話。但人們都說,只要看看需求文檔,裡面的名詞就是對象的候選者。對於後來成為 OOP 的概念也出現過許多批評。

由於面向對象的這種荒謬的模仿,許多人選擇了完全拋棄面向對象,轉而重新擁抱數學上的聖杯,即函數式編程。函數式編程不會作擬人式的地板和電梯,而是用向量表示電梯的位置和樓層的位置,將這些向量傳遞給某個分析演算法,同時傳遞呼叫按鈕的狀態向量。這些向量的組合就會產生一個動作描述,控制哪部電梯應該停在哪一層。

談論數學的人總是看上去比那些不會數學的人聰明些。而真正理解你所談論的數學會令人欽佩。當人們說「單子不過是自函子範疇上的一個幺半群而已」(a monad is just a monoid in the category of endofunctors)這句名言時,如果你確實能理解這句話,那麼恭喜你,你是為數不多的聰明人之一。

所有這些編程的形式中都包含了類型,以針對程序的正確性做出某種程度的自動推理。事實證明,如果你真的追求簡單,那麼編寫用於抽象對象創建的類型(class),或者用於抽象數據結構之間的數學屬性的類型(Haskell 的 typeclass)實際上是很困難的。讓那些想把事情做好卻受教育程度不高的人使用有類型的語言,通常會導致類型爆炸。類型 A 和類型 B 可能有 50% 的功能是一樣的,因為它們都是某個底層類型 C 的特殊實現(ad-hoc realization),但發現底層抽象 C 可不是簡單複製粘貼功能相同的那 50% 代碼就能做到的事情。當 A 和 B 以某種不應該出現的方式耦合在一起時,這種特殊繼承就會導致極其脆弱的代碼。

Alan Perlis 的一句著名的格言說:「100 個函數操作同一個數據結構,要好過 10 個函數操作 10 個數據結構。」這個簡介的斷言通常可以由抽象代數中的概念論證。但是,找到一個能支持 100 個有用的函數的數據結構有時很簡單(第一個想到的就是列表),有時卻十分困難(程序的輸入輸出)。


提議

讓系統設計能夠被眾包,從實用性、美學和性能方面都很有價值。

還有道德價值。

系統的組成結構應該盡量模仿人類。我的意思是,組件應該擁有目的,或者說擁有直接的責任,它們的交互應該可以類比為兩個或多個人類之間有意義的交互。用這種方式分解系統,任何熟悉系統的人就能像敘述人類行為一樣描述系統的行為。同時這種方式還能讓非技術的人推理這個系統。這就保證了更多的人能夠更現實地使用他們用了一輩子的辦法:考慮人與人之間的關係,藉此來找到系統設計問題的解決方案。這就是系統設計的民主。這就是面向故事編程。

這種想法並不是我的原創,只不過是前面的思想在我腦海里的轉述而已。James Coplein 在 GOTO 2017 大會上有一次演講,他說原始的概念化 OOP,就是用戶思維的本體直接反映到系統設計的本體。如果在屏幕上看到一隻狗,那麼就應該能在代碼中操縱(以某種形式)一隻狗。這直接反映出某些面向對象設計的荒謬(如角色走向牆的對象,然後牆對象告訴角色兩者碰撞了)。這個思想引發了我的一連串思考。

這次思考還產生了些新的東西,就是它內在和外在的同像性(homoiconicity)。YouTube 上有許多 Alan Kay 的談話,他談到了他最初關於規模擴展的想法來源於已經支持擴展的東西:人體的細胞模型。人體和大腦是個單例組件(singular component)在巨大規模的深度網路中能很好工作的典型例子。但是,並沒有哪個細胞比其他細胞更「聰明」,這很像是我們談論某個人比其他人聰明一樣。

這給我帶來了一個問題:如果人體本質上是生命對組成細胞的某種分布和民主,那麼其他形式的民主也許可以用來創造大規模的人造物。Alan Kay 博士提議,在計算機系統中像細胞那樣將任務民主化成組件,就能催生可擴展的系統。這無疑是給九十年代的 OOP 一記響亮的耳光,後者在某些情況下顯然並不成功。回到 OOP 的原始概念來,重新思考下。這次不想像成細胞,而是將系統換成一系列角色、精靈、人類或者隨便什麼東西,讓他們參與到故事中,我們就創造了一種推理系統,幾乎任何人都可以參與這種推理。用擴展系統的同樣方式,我們擴展了系統設計的過程。


反對意見

在構思並充實這篇文章的過程中,我收到了許多內部的反對意見,在這裡我想一一介紹下。這些意見分為兩種:美學的,和實用的。美學上的反對意見,指任何可變的因素(美學)造成的偏見。實用的反對意見,指那些認為某些提議由於社會限制或心理限制從而無法實現的觀點。


美學

歸謬法,滑坡謬誤,如果在一個地方做了,那麼每個地方都要做。

這種觀點認為,一切事物,甚至包括最簡單的一個數字,都應該是個角色。我也不知道是否應該同意這種觀點,我只是展示下這種觀點而已。

現實的宇宙似乎已經證實了,交流性的解構可以一直進行下去。我們可以說(實際上確實在說)夸克互相交換帶有顏色的膠子。質子、電子等帶電粒子交換光子。這些例子用人類的交流或意圖來比喻極其簡單的現象。

有個現象領域與物理沒有任何關係。正規思維體系的產物,比如數學,是不存在於物理世界的。這可以作為一條明確的分界線,判斷某個事物是否應該作為角色,或者作為實體過程。

因此,我們不會說數字 5 跟另一個數字 5 說「相加」。相反,我們會直接用那個正規領域的語法結構。類似地,集合論有它自己的組合數學,範疇論也是,高中代數也是,這些思維體系都構成了封閉且自我約束的推理系統,不需要任何其他交流方式。

關於這個問題的一點警告是,必須謹慎地理解,純凈性很容易被破壞。你可以把數字和字元串當做純粹的數學結構來處理(因此不需要通過交流性的結構去分解它們),但一旦它們的純粹性被打破(如一個可改變的字元串,或者很大的數字導致必須考慮內存分配的問題),那麼就又進入了與物理有關的世界,可以再次進行敘述性解構了。

一個與現實世界關係更緊密的場景就是 HTTP Web 伺服器的例子。HTTP 協議規定了位元組必須以某種規定的格式傳輸。這種格式由字元串組成,進一步又規定什麼才能組成字元串。但是,協議的其中一部分可以不遵循這種格式,比如請求體。實際過程中,你可以將請求看作一個字元串,併當做純粹的數學實體來處理。這種方式一般沒有問題,但當你遇到真實世界的限制,如很大的請求體導致耗盡所有內存,或導致性能問題的時候。這種限制會打破字元串的抽象,顯露出請求的本質:對網線上傳輸的電信號的一種解釋而已。在低層次上對其進行處理,還反映了現實世界的物理過程的本質。因此,應該避開字元串抽象,將請求看作一個角色,它能與底層進程交流其位元組,這樣就能更精確地討論前面提到的優化過程。

這些概念的一種迷人的組合形式就是,首先用敘述性方式設計一個子系統,然後,如果它的內部交流可以被安全地抽象,就可以創建一個庫,把流程當做數據對象來處理,用流程填充整個系統,並以純粹的方式交換這些流程。這使得程序員可以根據實際場景,在純粹的操作和敘述性操作之間做出選擇。另外,可以像企業之間的交易那樣,一組子處理的集合可以根據某個獲得共識的通信協議,以抽象的方式把其他子處理的集合當做一個宏處理。

數學比自然語言更簡單。數學模型讓事物簡化,自然語言讓事物晦澀。

有一種美學的觀點是,交流應該以儘可能簡潔的方式完成。然而簡潔通常很難達到。簡潔的事物通常會傾向於通用。數學語言很簡潔,而正由於其簡潔,它變得很難理解。當然,哲學評論家會說最簡潔的語言是最通用的語言。但這與實用性是矛盾的。

人類並不是一生下來就懂得數學。人類學習的第一種語言工具是完全混亂的,充斥了各種例外和不規則。這就是自然語言:各種語調,語氣,還有語境。在這之上,年輕人必須學會剝離語境,用正式的抽象語言進行交流。這種技能是許多人根本學不會的。那麼從實用角度來說,不論是抽象數學還是具體數學,儘管它有潛力成為通用語言,但它需要一定水平的能力或智力來達到流利的水平,這就阻礙了它成為民主的語言。

一個利用數學作為通用語言的基本例子,就是編程語言的交流和副作用。這方面的許多事物,如時間、空間、分歧,都可以用某種記號來表示,在應用範疇論中稱為單子(monad)。如果你用過某種新聞閱讀器(如 Hacker News)閱讀編程的文章,那麼你對這個詞應該不陌生。你還可能遇到過函子(functor)、加強函子(applicative)、類型類(typeclass)、組合(composition)、恆等(identity)、密度(density)、端(end)、錐(cone)、同構(isomorphism)、anamorphism、catamorphism、米田引理(yoneda lemma)、kan 擴展(kan extensions)……

這就是最棘手的部分。絕大多數人連順利地閱讀範疇論中的語言都需要花費很長時間,更不用說讓他們具有足夠的水平去順利地應用這些語言,或者用這些語言與他人交流了。對於大多數人來說,數學就是個怪物,完全是看不懂的符號語言。他們絕不會學的。

但是——這就涉及到道德了——為什麼他們會這樣?在計算機語言中,單子是概括帶有額外數據的函數組合的正式數學結構了。其中的「額外數據」正是交流中晦澀的部分。任何輸出為單子的函數都在正常的結果之外交流了某些東西。交流的東西可能是失敗,可能是日誌,也可能是為某個外部系統進行針對交流的描述。這些交流可以用單子組合有意義地連在一起。也就是說,單子能用抽象、通用、數學的方式概括所有交流。

我必須解釋一下。我得解釋下單子。大部分程序員都知道怎麼說話。他們或多或少都明白人類之間有關係,這些關係通過交流進行。這些都是我們的本能,使用這些能力都是下意識的。所以為什麼我要用另一種方式跟別人說,「嘿,你有一種強大的能力,我來告訴你怎樣才能用你理解不了的方式來交流」?我不知道該怎麼回答。單子的抽象對於我這樣擁有分析思維的人來說的確很美,所以我錯誤地假設所有人都有同樣的美學觀點。

最後,我認為對於這種美學慾望的最後一擊就是關於對問題的數學表現形式是否屬於過早優化的爭論。對於不想關心無關細節的人來說,數學表現形式遠遠不是最準確的優化。儘管墨菲定律對於這些無關細節能保持無關多久給出了時間限制。如果你很幸運,那麼你能遇上當抽象被破壞時,你要維護的代碼恰好不存在了。不過更多的情況是,你越是激進地抽象,得到報應就越快。

函數 f(x, y) = x + y 完全沒有問題,除非 x 和 y 不同時存在。這種情況有個單子可以描述(future/stream)。或者無法從同一個來源獲得。也有個單子可以描述(environment/reader)。能理解單子的人真的很少。能理解單子變換(monad transformer,組合單子的效果)的人就更少了。在沒有類型檢查的語言中實現單子變換就是個災難。所以,一旦將美麗的數學抽象強行引入到新的需求域,事情就一下子變得複雜了。


務實

除了這些美學方面的考慮之外,還有一些實用的反對意見。

愚蠢和自大

對於面向故事編程概念的一種可能的反應是:像我給孩子講故事一樣討論系統的組件太難為情了,我自己也有過這樣的經歷,當我大聲地向同事講述小精靈的故事的時候,覺得自己好傻。仔細想想,這也並不意外。這種情況下,系統的組件不是深刻而又細緻入微的人類,所以討論它們不像討論《警犬追殺令》那麼簡單,除非我們討論一些演員欠缺的方面。但是,無論預期結果怎樣,要想重新構建問題讓任何孩子都能解決,那都是一件駭人聽聞的事情。解構的過程需要將門檻降到最低,比如 5 歲的孩子都可以做到。

害怕難為情,犯傻,或自大都不是好的理由,我們要排除萬難。如果你是個非常聰明的人,而且你發現自己很難放下做好一切事情的想法,那麼你可能會導致已然艱難的軟體推理雪上加霜。

屈尊

我所想到的第二個反對意見是過渡的實際問題。由於分解技術的解構特性,我們很容易給非技術人員解釋系統問題,因此他們可以提供自己的敘述推理。然而,可以想像到在一些組織中,如果你聽到通常使用行業術語的人突然用給孩子講故事的口氣講話,會感覺有點自降身份或屈尊。

如果只對一小撮明白這種練習帶來的價值的人進行練習,那麼相對會容易一點,同事要培養外部的人,或只向非技術人員(項目經理等)解釋其中的好處,至於由誰來討論問題並不重要。語言並不會減弱對聽眾的闡述,它可以降低門檻。

身份動態

最後一個我想到的顧慮是純粹與不純粹的考慮。有時人們會依靠數學來解決人事問題。方程不會涉及時間,也不會論及性別或種族。

優秀的代碼庫曾引起過爭議,主要是圍繞身份,美國黑人之外的「主人/僕從」等概念上,或人們在代碼中(0x8008135)不恰當地引用性別概念。這是一個值得關注的問題,除了非常正規的數學世界以外,其他非科學系統的討論中也必然會摻雜人們所關注語言編碼問題。在文本上述的故事中我只有一位女性代詞的角色。

我對於這種批評的回應是:這些問題超出了我提出的解決方案。僅通過參與色盲或無性別差異編程,無法改變你的種族歧視或性別歧視。微軟的種族主義者 Tay twitter 聊天機器人就是一個例子,我們構建的系統擴展並將網路用語編碼到系統中,因而產生了這種機器人。為你的系統排除這些憂患意味著排除系統產生的問題,也就是你的團隊。


結論

本文介紹了面向故事的程序設計,以及值得嘗試的原因。坦白來說,這個想法對我來說似乎非常激進,因為面對那些希望數學成為計算機通用語的人群時我會華麗麗的倒下。鑒於與通用語相關的產品以及這種產品周圍的系統的優點以及其與生俱來的無法改變規模的特性,我想用這篇文章來表達自己完全站在對立面的立場。

也許這可以引起你的共鳴。如果真是這樣,我鼓勵你大膽嘗試。我肯定會在自己的個人項目中將這些想法付諸實踐。之後,我會勇敢地在公司和產品系統內開展關於這些概念的更大的討論。這種方法的意義非常深遠,所以他們帶來的改變將席捲全球。

人類善於敘述。我們可以將敘述直接編入我們的軟體方法中。讓我們進入面向故事的編程時代。


原文:http://www.brandonkeown.com/2018/05/story-oriented-programming.html

作者:Brandon Keown

譯者:彎月,責編:楊麗

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

中國軟體行業協會智能應用服務分會在京正式成立
百萬 Android 用戶受感染!

TAG:CSDN |