函數式編程和面向對象
Ⅰ 為什麼說面向對象編程和函數式編程都有問題
簡言之,不論是面向對象編程還是函數式編程,如果你走了極端,那都是錯誤的。面向對象編程的極端是一切都是對象(純面向對象)。函數式編程的極端是純函數式編程語言。
面向對象編程的問題
面向對象的問題在於它對「對象」的定義,它試圖將所有事情就納入到這個概念里。這種做法極端化後,你就得出來一個一切皆為對象思想。但這種思想是錯誤的,因為
有些東西不是對象。函數就不是對象。
也許你會反駁,在Python和Scala語言里,函數也是對象。在Python中,所有的含有一個叫做__call__的方法的對象其實都是函數。類似的,在Scala語言里,函數是擁有一個叫做apply方法的對象。但是,經過認真的思考後,你會發現,它混淆了源祖和衍生物的概念。函數是源祖,包含函數的對象實際是衍生物。__call__和apply它們自身首先就是要定義的所謂「函數對象」。Python和Scala實際上是綁架了函數,把它們監禁在「對象」里,然後打上「__call__」 和 「apply」 標簽,把它們稱作「方法」。當然,如果你把一個函數封裝到對象里,你可以像使用一個函數那樣使用對象,但這並不意味著你可以說」函數也是對象「。
大多數的面向對象語言里都缺乏正確的實現一等(first-class)函數的機制。Java語言是一個極致,它完全不允許將函數當作數據來傳遞。你可以將全部的函數都封裝進對象,然後稱它們為「方法」,但就像我說的,這是綁架。缺乏一等函數是為什麼Java里需要這么多「設計模式」的主要原因。一旦有了一等函數,你將不再需要大部分的這些設計模式。
函數式編程的問題
相似的,函數式編程走向極端、成為一種純函數式編程語言後,也是有問題的。為了討論這個問題,我們最好先理解一下什麼是純函數式編程語言。出於這個目的,你可能需要閱讀一下Amr Sabry先生(他是我的博士導師)的What is a Purely Functional Language。概述一下就是,純函數式編程語言是錯誤的,因為
有些東西不是純的。副作用是真實存在的。
所謂純函數,基本上就是忽略了物質基礎(矽片、晶體等)表現的特性。純函數式的編程語言試圖通過函數——在函數中傳入傳出整個宇宙——來重新實現整個宇宙。但物理的和模擬的是有區別的。「副作用」是物理的。它們真實的存在於自然界中,對計算機的效用的實現起著不可或缺的作用。利用純函數來模擬它們是註定低效的、復雜的、甚至是醜陋的。你是否發現,在C語言里實現一個環形數據結構或隨機數發生器是多麼的簡單?但使用Haskell語言就不是這樣了。
還有,純函數編程語言會帶來巨大的認知成本。如果你深入觀察它們,你會看到monads使程序變得復雜,難於編寫,而且monad的變體都是拙劣的修改。monads跟Java的「設計模式」具有相同的精神本質。使用monad來表現副作用就像是visitor模式來寫解釋器。你是否發現,在很多其它語言里很簡單的事情,放到Haskell語言就變成了一個課題來研究如何實現?你是否經常會看到一些有著諸如「用Monadic的方式解決一個已經解決的問題」這樣標題的論文?有趣的是,Amr Sabry先生一起合著了這樣一篇論文。他試圖用Haskell語言重新實現Dan Friedman的miniKanren,但他不知道如何構造這些monads。他向Oleg Kiselyov——公認的世界上對Haskell類型系統知識最淵博的人——求教。而且你可能不知道,Amr Sabry先生應該是世界上對純函數編程語言知識最淵博的人了。他們在 Oleg 的幫助下解決了疑難後一起合著了這篇論文。諷刺的是,Dan Friedman——這個程序的原作者——在使用Scheme語言開發時卻沒有遇到任何問題。我在Dan的代碼基礎上重新實現了miniKanren,增加了一個復雜的負操作。為了實現這個,我需要使用約束式邏輯編程和其它一些高級的技巧。鑒於用Haskell語言重寫基本的miniKanren將兩位世界級程序員都難倒了的事實,我不敢想像如果用Haskell的monads如何能實現這些。
有些人認為monads的價值在於,它們「圈定」了副作用的范圍。但如果monads不能真正的使程序變得易於分析或更安全,這種「圈定」有什麼用呢?事實上就是沒用處。本身就跟副作用一樣難於分析理解。沒有一種東西可以說monads能使其簡單而靜態分析辦不到的。所有的靜態分析研究者都知道這點。靜態分析利用了monads的本質,但卻去除了程序員編寫monads代碼的負擔——而不是增加負擔。當然,過度的副作用會使程序很難分析,但你也可以使用C語言寫出純函數,例如:
int f(int x) {
int y = 0;
int z = 0;
y = 2 * x;
z = y + 1;
return z / 3;
}
你用匯編語言也能做到這些。純函數並不專屬於純函數式編程語言。你可以用任何語言寫出純函數,但重要的是,你必須也應該允許副作用的存在。
回首歷史,你會發現,數學上的理想主義是純函數編程語言的背後推動力。數學函數簡單漂亮,但不幸的是,它們只是在你構建原始純粹的模型時才好用。否者它們會變得很醜陋。不要被「范疇論」等標語嚇倒。我對范疇論了解很多。即使是范疇理論學家自己也稱其為「抽象無意義」,因為它們基本上就是用一種怪誕的方式告訴你一些你已經知道的事情!如果你讀過Gottlob Frege的文章Function and concept,你會吃驚的發現,在他的這篇論文前的大多數數學家都錯誤的理解了函數,而這僅僅是剛剛100多年前的事。事實上,數學語言上的很多事情都是有問題的。特別是微積分方面。編程語言的設計者們沒有理由要盲目的學習數學界。
不要盲目的愛上你的模型
無論任何事情,當走向極端時都是有害的。極端化時,面向對象編程和函數式編程都試圖把整個世界裝入它們的特有模型中,但這個世界是在完全不依賴我們的大腦思考的情況下運轉的。如果以為你有一個錘子,就把所有東西都當成釘子,這明顯是不對的。只有通過認清我們的真實世界,才能擺脫信仰對我們的束縛。
不要讓世界適應你的模型。讓你的模型適應世界。
Ⅱ scala是函數式編程和面向對象編程結合的語言,這兩種編程的特點分別是什麼
函數式編程或稱函數程序設計,又稱泛函編程,是一種編程范型,它將電腦運算視為數學上的函數計算,並且避免使用程序狀態以及易變對象。函數編程語言最重要的基礎是λ演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值)。
比起命令式編程,函數式編程更加強調程序執行的結果而非執行的過程,倡導利用若干簡單的執行單元讓計算結果不斷漸進,逐層推導復雜的運算,而不是設計一個復雜的執行過程。
面向對象程序設計是一種程序設計范型,同時也是一種程序開發的方法。對象指的是類的實例。它將對象作為程序的基本單元,將程序和數據封裝其中,以提高軟體的重用性、靈活性和擴展性。
目前已經被證實的是,面向對象程序設計推廣了程序的靈活性和可維護性,並且在大型項目設計中廣為應用。 此外,支持者聲稱面向對象程序設計要比以往的做法更加便於學習,因為它能夠讓人們更簡單地設計並維護程序,使得程序更加便於分析、設計、理解。反對者在某些領域對此予以否認。
當我們提到面向對象的時候,它不僅指一種程序設計方法。它更多意義上是一種程序開發方式。