正文開始前,分享一段個人非常喜歡的演講:Philip Wadler的『Propositions as Types』:
其中有一段很讚的話:
Computer Science. There are only two things wrong with it: Computer and Science.
身為CS出身的碼農真的是被婊到心坎裡 …
這句話背後的意義是,我們真正在做的事情,其實是資訊的構築與轉換,計算機或程式語言不過剛好是我們用來處理這些問題的工具,也因此他認為較適合的名稱其實是Informatics之類的東東。什麼意思呢?例如Int版的toString如果用Haskell type annotation來寫,大概是
toString :: Int -> String
也就是從一個結構為整數的資訊到一個結構為字串的資訊的轉換。sort則是
sort :: Ord a => [a] -> [a]
從[a]到[a]雖然沒有發生資訊的類型轉換,但輸出的[a]會是排序過的。這件事通常不會表現在type annotation上,但硬要幹的話應該也不是不行;例如定義一個type叫OrderedArray,上式就變成
sort :: Ord a => [a] -> OrderedArray a
雖然骨子裡我們做的是如此充滿數學與工程的事情,也許正因為語言扮演要角吧?寫過幾年程式後,大家都會發展出自己的風格,為了避免一個code base被搞得像超現實主義的大雜燴,於是有了各式各樣的coding standard來做基本規範。一個好的coding standard,個人覺得會像是制定一個基本框架,讓大家能在維持基本結構統一的情況下發揮創意,就好像七言絕句或詞牌一樣,與各種programming paradigm交叉組合下,漸漸地寫程式這樣聽起來如此geek的事情,本質上好像與詩更為貼近了。
可能因為個人偏好functional programming吧?我寫程式喜歡以函式為單位開始,而不管用哪個語言,我寫一個函式的原則是兩易一難:易讀、易測、難誤用。
何謂易讀?
易讀與否其實是一個很主觀的問題。對我來說,所謂易讀大概就是兩點:
1. 我不須一直捲動程式碼即可掌握全貌
例如用Java或Swift寫一個class有兩種常見風格,一種是按照visibility來整理:所有public的東西放一區、protected的放一區、private的東西放一區;另一種則是按照關聯性來整理,例如一個public介面用了一個private helper function或一個protected constant,這些東西就會被放在一起。兩種整理方式都有其道理,但我個人偏好後者,因為我記憶力很差,實在受不了讀一個函式要一直來回捲動來找參照到的東西。
2. 不要用太聰明的語言技法來撰寫
呃…這也蠻主觀的。我的原則大概是盡量讓input與output的意義明顯,讓閱讀的人不需要花太多力氣就可以了解這個函式的意義是什麼。舉例來說,現在很常見的JS object spread operator就很容易寫出『太聰明的函式』,例如我們常常看到這種redux action creator:
const fooAction = ( args ) => ( { type: JUST_A_FOO_ACTION, ...args, } );
這個action creator非常general,單看這裡我只能推論對應的reducer應該是舉凡JUST_A_FOO_ACTION來的東西一切通吃,偏偏事情通常是這樣:
const fooReducer = ( state, action ) => { if ( action.type === JUST_A_FOO_ACTION ) { return { foo: action.foo, bar: action.bar, }; } };
這還只是兩層,如果有好幾層的function call全部都spread來spread去,光是要知道一個key-value是怎麼來的就有得trace了。以上例來說,fooAction應該要這樣寫會更加清楚,也不會被誤用:
const fooAction = ( { foo, bar } ) => ( { foo, bar, } );
我了解spread operator有它必須的時候,特別是在寫library code而非application code的時候,但我覺得必須了解它代表的語意是『將object A所有的properties都以相同的key賦至object B中』,而非因為很方便就濫用。
何謂易測?
這就比較客觀了:
- 容易寫unit tests測試
- 容易debug
前者通常衍生出來的性質是不仰賴特定全域變數、pure等等。例如WordPress裡面有很多函式是這樣:
function foo() { global $wpdb; $result = wpdb-> ... }
這種code對我來說就很糟,想像看看這個foo要怎麼寫unit tests?光是要mock database和抽換$wpdb就是一大工程。
至於後者,以imperative programming language來說就是容易step by step trace,可以是debugger,也可以是塞log。因此,對我來說
const foo = ( value ) => { const stagedResult = someTransform( value ); const yetAnotherOne = oneMoreTransform( value ); const finalResult = combineTheTwo( stagedResult, yetAnotherOne ); return finalResult; };
會比這個來得好:
const foo = ( value ) => ( combineTheTwo( someTransform( value ), oneMoreTransform( value ) );
如果哪天foo壞了,前者不管要塞log還是用debugger都非常輕鬆,但後者如果想要log常見的方法是先寫個這玩意
const wtf = ( arg ) => { console.log( 'wtf: ', arg ); return arg; };
用debugger也很容易操作錯誤,因為會需要不斷step in、step out、inspect return values,一不小心按錯就重來。
但經驗上,我寫前者一定被reviewer電到爆,所以我都從善如流寫後者 (淚)
何謂難誤用?
想像自己在經營一家餐廳,有一天其中一間廁所有了靈性,如果有超過10年經驗的碼農進去大X,馬桶就會悲憤爆炸,怎麼辦?把那間廁所上鎖不要讓人用就行了。
咦?可是又不是有那麼多超過10年經驗的碼農會去大X啊?問題是只要有一個進去就game over了啊,滿地金湯的餐廳能看嗎?函式也是一樣,如果是無法處理的input,最好的方法就是讓它不可能進去,不然就是要把它濾掉。
很遺憾的是,這件事很難在JS或PHP這類dynamic language做到盡善盡美。例如有時候我們會看到這種code:
function foo( $val ) { $val = is_int( $val ) ? $val : 0; ... }
如果不是primitive type,而是composite type像array或object呢?檢查的cost太高,寫起來又麻煩,根本不會有人這麼做。常見的方法是用JSDoc之類的東西寫下一個function預期的input與output,但compiler並不因此就會保證這件事,所以使用者仍然可以『合法誤用』,因此稱不上完善。而且,如果把親愛的null和undefined考慮進來就更有趣了吧?我相信正在讀這篇文章的人,或多或少都被預期外的null炸過個幾次而怒吃雞排。Haskell或Elm選擇用Maybe這樣的product type來處理這個問題,C/C++的pointer type可以做到類似的事情,但效果並不好,因為
function foo( SomeClass* );
SomeClass*這樣的pointer type代表的語意其實是一個『dereference後會得到SomeClass的type』,但因為null pointer的存在,它多了一個『存在與否』的語意,寫過C/C++就會知道這樣看似小的尷尬語意能造成多少麻煩。
回到JS與PHP,個人覺得能做的大概就是primitive type有必要檢查就檢查,如果有值域的限制那就一定要濾掉値域外的數值,如果是object type那至少要意識到null,最後就是一定要寫unit tests來確保以上『語意保證』,畢竟dynamic language的compiler是不會幫你做這些事情的。
Code is Poetry
前面提到寫程式碼對我來說是件很有詩意的事情。當在一個一卡車人協作的code base工作時,這更像一首著意和諧的詩。如何找到個人風格與他人風格的平衡點?如何讓基本框架有效又不過於抑制個人發揮?又理性又感性,又獨特又和諧,種種激盪在開源的發展下愈發精彩。找到屬於自己,能帶到各種語言、各種專案應用的『武器』,或許正是這個時代當個碼農的醍醐味吧。
那演講好硬呀~
同為CS出身,我也覺得在學時有種怪怪的感覺,連問題都還沒好好先了解(敵人是誰、什麼情境下有仇),先教你用什麼工具可以解(不要管,拿著這把古靈精怪槍就對了)。
難怪一堆應用數學系出身的一碰到程式語言,跟化學反應變身怪物一樣,衝很快!
是說田大有在美國唸過書嗎?還是說有強化學習英語能力的方法能分享呢?
最近一直在想,如果要辦大場一點的活動,勢必也要有對應國際化的能力,這塊還有很大的進步空間Orz
btw, 你們家除了「CODE IS POETRY」是常見,還有「Silence is golden.」藏在檔案開頭註解的,也有這件衣服嗎?XD
讚讚
沒有在國外念過書哩,我後來是靠線上一對一語言學習平台來補強。在加入a8c前是靠CafeTalk: http://cafetalk.com/,加入後因為公司有補助Lingo Live,就跳槽了。
TIL Silence is golden … (躲)
工商服務一下,有興趣看有哪些衣服的話可以看這裡:https://mercantile.wordpress.org
讚Liked by 1 person
哇 原來如此,實在好厲害R! 這學習資訊真讚~ 筆記一下,感謝大大分享XD
常看你拿到 WordPress 周邊小物,其實亞洲區推廣大使就是你吧!哈哈
不過用買的好像少了什麼,靠努力,想辦法凹到來 A_A
(首先從貼紙開始 (誤
讚讚