所以函數式編程是什麼(ㄧ)

所以函數式編程是什麼(ㄧ)

更新於 發佈於 閱讀時間約 5 分鐘

前面談了那麽多函數式編程與物件導向的差異,但我們還沒定義函數式編程。就像物件導向,函數式編程沒有明確的定義,每個人對於什麼是函數式編程都有不同的看法。在這裡我會總結前面的討論,給出我對於函數式編程的觀點。


物件導向注重封裝與延展性,因此一般基於三個機制:繼承、多型、封裝,它們代表了物件導向所重視的部分。而函數式編程著重於規則,認為規則的定義應該要明確且嚴格,因此它特別重視實現規則的機制。我認為的函數式編程則基於三個機制:類型、泛型、多型。


「類型」的機制指的是每個變數應該要有唯一的類型,它用來描述值的資料結構。它同時可以用來描述資料的意義,因此不是結構一樣就是相同的類型,也就是所謂的nominal type。函數式編程強調變數類型的明確性,因此不使用像是類別的抽象類型描述資料,而是透過多型描述抽象概念。相對地,為了讓類型具有延展性與可重用性,函數式編程使用複合類型組合多個類型,而非透過繼承延伸已有的類型。對於函數式編程,函式本身也有類型,而它只是其中一種複合類型而已,因此使用函式作為值使用是很正常的。函式的類型描述的是函式的行為,更廣義來說,函式的非同步標示符(async)、可拋出例外標示符(throws)等都可以算作函式類型的一部分。在Haskell則是使用Monad描述這些結構。總結來說,類型的職責是描述資料結構乃至程式結構,而非透明的特性能讓我們根據架構設計賦予這些結構唯一的語義。


「泛型」的機制透過參數化類型實現變數類型的抽象化。不同於物件導向的子類關係,它能保證類型之間的同調性,而這種同調性本身可以用來描述整體的結構。參數化的類型讓我們無法知道變數的具體類型,這使得我們必須以獨立於類型的方式處理它,除非程式語言本身允許實體類型檢查。這種必須與具體類型無關的限制形成對類型的抽象化,泛型本身並不能直接描述有意義的抽象類型,通常會搭配約束和多型的機制使用。如果程式語言不允許子類關係,那麽泛型就是唯一能使用非特定類型的方法,否則所有變數都只能有特定且具體的類型。對類型進行參數化代表把類型本身當作一種數值,並使用一個佔位符取代。更廣義的,不只有類型可以參數化,複合類型的建構子也能參數化,而它具有和類型不同的種類(kind),這種將類型作為值看待的方法使我們能更進一步地思考程式的抽象結構。總結前面的討論,泛型能將具體的結構從程式中抽離出來,變成可替換的抽象結構,這種特定的抽象就是這個機制的職責。


函數式編程的「多型」與物件導向的不同,它指的是特設多型,透過讓一個函式針對不同特定的類型做出不同的行為,讓我們可以描述抽象概念。特設多型能讓程式在不同地方對不同類型實作同一個概念,這為抽象概念提供擴展的能力。這種可擴展的語義讓我們無法直接透過具體類型決定函式的行為,因而形成抽象的概念。函數式編程的特設多型可以藉由對類型參數的約束實現,其中約束通常是使用介面/特性系統描述。對於不允許繼承和實體類型檢查的程式語言,這是唯一能讓同一個函式對不同類型具有不同行為的方法,而這與流程控制的分支不同,它的實際行為可以藉由實體化的類型決定,而不是在執行時期才決定。因此,多型的職責是為相同概念實現不同的行為,並讓我們在程式中實現與描述抽象概念。


函數式編程的這些機制與物件導向很像,但函數式編程的機制是根據能力和規則設計的,而非像是物件導向根據某種特定的面向設計,因此對於問題的解決方案比較不容易出現多種可能的實現方法。例如類型只能描述具體資料結構,不能描述抽象概念;泛型可以用來抽象化資料結構,但不會抹去類型資訊;多型必須藉由泛型與約束完成,它是唯一能實現擴展抽象概念的機制。而物件導向的一些機制對於函數式編程是多餘的,甚至是有害的。例如子類關係讓我們難以判斷變數是否是抽象的;變數的實體類型檢查讓泛型失去行為的一致性。相對的,函數式編程的機制之間的能力是沒有重合的,因此能將各自的能力發揮到極致。這種設計讓變數、函式、介面等編程實體僅具有最少的能力,使得我們能更準確掌握各個部分的程式碼能做什麼和該做什麼。這種最小化能力、最大化用處的設計就是我認為的函數式編程最重要的原則,而這三個機制從語言級別上遵從與支援這個原則。

avatar-img
have bear的沙龍
4會員
28內容數
這不是教你如何從物件導向到函數式編程的入門教程。我會深入探討物件導向與函數式編程的差異,並討論為什麼你應該使用函數式編程並徹底放棄物件導向。
留言
avatar-img
留言分享你的想法!
have bear的沙龍 的其他內容
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。 Single Responsibility Principle 是「單一職責原則」,認為一個模組
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。 Single Responsibility Principle 是「單一職責原則」,認為一個模組