來源 | Cocoaphony
譯者 | 蘇本如,責編 | 夕顏
封圖 | CSDN下載自視覺中國
自從Swift問世以來,我一直看到一些奇怪的評論,認為Swift是一種函數式程式語言。我有點納悶為什麼有人會這麼說,因為Swift幾乎沒有什麼「函數」。它是一種非常傳統的面向對象語言,著重於泛型編程。
我的推測是,人們使用一個特性列表來確定一種語言的範式。但我們使用範式這個詞是有原因的。
範式——以某一特定科學科目的理論和方法論為基礎的一種世界觀。
「一種世界觀。」是的,這就是它的本質所在。
程式語言範式很像音樂流派。它們是混亂的東西,我們可以爭論在哪裡畫線,什麼去哪裡,什麼是純粹的以及什麼是融合。但就像古典吉他和重金屬屬於不同的流派一樣,即使他們都使用吉他。
我有些朋友說他們對音樂很有鑑賞力。他們標榜自己聽「一切」的音樂。從Jimmy Buffet的鄉村搖滾到Metallica的重金屬音樂,這些樂隊演奏的所有音樂他們都喜歡。許多程式設計師也是如此。他們只知道過程編程和面向對象編程的範式。他們認為BASIC和Java之間的區別就是所有語言之間的區別了。當他們遇到一種新的語言時,他們的第一個問題是「它的語法是什麼?」?但是我認為他們的問題應該是「它是如何看待問題的?」
我現在要說很多關於編程範式的事。像音樂流派一樣,有很多觀點和方法可以對音樂進行分類。我的方法不是唯一的方法。但是有些方法比其他方法更加有用。我想表達的是,因為它們都有map而將Swift和Haskell歸為一類程式語言的分類法,並不比因為它們都有vocalists而將Wangga、Opera、和Rock歸為一類程式語言的分類法更有用處。
過程式(或命令式)編程主要關注的是將問題分解為一系列的動作。它通常的結構是「執行第一步,然後重複執行第二步,直到某件事是真的,然後執行第三步。」這種範式在流行的程式語言中非常普遍,以至於許多程式設計師認為這就是編程的全部意義。這當然不是。它只是分解問題的一種方法。當面對一個問題時,過程式編程的問題是:「我需要執行哪些步驟來解決這個問題?」
面向對象編程(OOP)主要關注的是將問題分解為具有屬性的自包含對象,以及操作這些屬性的方法的集合。它通常的結構是帶有實例(對象)的類的層次結構,這些實例繼承了屬性和方法。當面對一個問題時,面向對象編程的問題是「需要什麼樣的對象一起工作才能解決此問題?」
這兩種思維方式在流行的程式語言中極其普遍,並且可以很好地協同工作。自從第一批機器語言問世以來,過程式編程就一直伴隨著我們。即使是早期的自動織布機也是在過程式範式下工作。
自上世紀90年代以來,面向對象編程(OOP)一直是編程的主導。長期以來,它一直是主流的範式,並且一直長時間地主導著CS程序,以至於許多程式設計師都認為這是必然的。他們認為只有像Fortran這樣的「古老語言」才會缺乏對象(而現在,甚至連Fortran都支持對象了)。
但是面向對象編程(OOP)只是思考問題的一種方式。函數式編程是思考問題的另一種方式。函數式編程主要是將問題分解為接受並返回不可變值的函數。它通常的結構是一些將值轉換為其他值的函數,以及各種組合函數的方法的集合。它避免了可變狀態,並且不要求函數的求值以任何特定的順序進行。函數式編程將程序視為一個數學問題,而不是一系列操作。當面對一個問題時,函數式編程的問題是「需要以何種方式來轉換什麼樣的值以解決此問題?」
當你第一次開始使用Swift時,你首先要找的是什麼?也許是它如何處理類和協議的?也許是如何調用方法,聲明和分配變量,定義屬性?或者是它們的for和while循環版本?這些都是面向對象編程和過程式編程的工具,你認為它們都是容易獲得和易於使用的,這是正確的。你只需要知道語法。
我聽到有人將Swift描述為「函數式的」,因此,當我打開我的第一個Swift工作區時,我立即查找Swift中的flatmap的用途。我想知道它是怎麼用來將一個列表拆分為頭和尾的。我查找了一個foldLeft等效且不可變的集合。Swift似乎對它們都沒有進行特殊處理。這並不是說一門語言必須具有這些才能被稱之為函數式語言,就像一門語言必須具有for循環才能被稱為過程式語言一樣。
但是,如果我向你展示了一種新語言,並且說它是面向過程和面向對象的,但是你必須使用if和goto來實現for循環的功能,並且沒有類繼承,那麼你可能會對這種語言的特性選擇感到驚訝。一個無法在O(1)時間內簡單地將一個列表折分出「第一個」和「不是第一個」元素的「函數式」語言是一種非常奇怪的函數式語言。
Swift特有reduce和map函數,它還具有第一類函數和模式匹配。並且,它還具有在其它函數式語言中也很常見的一些特性。它的語法甚至感覺與Scala非常相似(當我意識到關聯值是樣式類(case class)時,關於它們的一切就變得有意義了)。但是它並不像其它函數式語言那樣思考問題。它們鼓勵你在任何可能的地方使用let,但是在一個Swift程序中,你總是會有let和var的混合,並且蘋果提供的大多數示例都包含變量。在Scala中工作時,我幾乎從不使用可變變量。在Haskell中,可變變量被認為是高級功能,甚至都沒有出現在介紹性的書籍中。
這就給我們帶來了真正的差異。在Swift中,你基本上是以一個過程式編程/面向對象編程的範式進行工作,並且整個語言都是圍繞這個範式構建的。當有需要時,有一些工具可以讓你跳到函數編程樣式(但沒有函數式編程的全部功能)。在Haskell中,你基本上是以函數式編程的範式進行工作。有一些可用的工具(Monad)可以讓你在需要過程編程樣式(沒有過程編程的全部功能)時,跳轉到它。
有人可能會說:「Rob,你太直白了。Swift是一種多範式語言,它包含了面向對象編程和函數式編程。」這是一派胡言。Scala才是一種多範式的,面向對象編程/函數式編程。當你在Scala中處理一個問題時,你將其分解為主要在不可變數據結構上工作的對象。這才是面向對象編程/函數式編程的做法。而在Swift中,他們甚至連提供一個不可變列表都不願意。並不是說他們無法添加它,而是因為這並不是Swift工作的根本。
Swift是一個多範式程式語言,但它不是面向對象/函數式編程的。而是面向對象/泛型編程的。泛型編程主要關注可應用於任意類型的通用算法。它與函數式編程有一些相似之處,當然也有這樣的程式語言,它既是函數式的,又是泛型的,但是泛型編程不在乎算法是函數(接受並返回不可變值的東西)還是過程(改變狀態的事物)。你可以說Swift不是泛型的,因為它有Array<Int>(整形數組)。你可以用非泛型語言實現這種結構。你也可以說Swift是泛型的,因為你可以在整個核心庫中找到泛型編程的程序。考慮像advance這樣的函數:
/// Return the result of moving start by n positions. If T models/// RandomAccessIndex, executes in O(1). Otherwise, executes in/// O(abs(n)). If T does not model BidirectionalIndex, requires that n/// is non-negative.func advance<T : ForwardIndex>(start: T, n: T.DistanceType) -> T
這正是你對泛型語言所期望的那種功能。它封裝了一種算法,該算法可以在任何實現ForwardIndex的對象上工作。但它不是ForwardIndex實例繼承或必須實現的方法。這似乎很微妙,但實際上是一種截然不同的思維方式。
你可以在Swift標準庫中看到很多這樣的東西,它們正是你希望在泛型語言中找到的東西。很多看起來像「函數」特性的東西實際上只是通用的算法。讓我們看看下面的quickSort和reduce函數:
func quickSort<C : MutableCollection where C.IndexType : SignedInteger> (inout elements: C, range: Range<C.IndexType>, less: (C.GeneratorType.Element, C.GeneratorType.Element) -> Bool)func reduce<S : Sequence, U>(sequence: S, initial: U, combine: (U, S.GeneratorType.Element) -> U) -> U
reduce是一個非常常見的函數工具,它就在quickSort函數旁邊,但是沒有函數式語言會以這種方式公開它(它改變了集合)。兩者都以泛型樣式表示。它們只是算法。一個可以改變集合,另一個不可以。重要的是算法可以在多種數據上重複使用,這就是泛型編程。我們得到了一些函數特性,但這其實只是它的一個副作用,而不是它的範式。
這些都不是對Swift的批評。沒關係,它不是函數式語言。我本來想,要是所有Cocoa開發都是函數式編程就好了,但這只是因為我喜歡它。我不知道這是否會進一步實現真正出色的iOS和Mac應用程式的目標(至少在短期內)。不是說FRP(函數響應式編程)不是個好主意。對此我沒有什麼強烈的意見。只是與Swift.2相比,ObjC程式設計師的學習曲線很高2,而我們需要將Swift與(非函數式的)ObjC集成很長一段時間。
我希望隨著時間的推移,Swift將會包含更多的函數特性。if和switch應該返回值。應該允許在模式匹配中將一個列表拆分成頭和尾(這點應該很輕鬆就能實現)。函數應該接受具有強制尾調用優化的「尾遞歸」屬性。可變方法應該很少用,不可變的數據類型應該更多。
這些東西都不會使得Swift自己成為函數式語言。但是足夠多的這些東西可以讓我們用更多的函數功能模式來編寫代碼,也許有朝一日,Swift和Cocoa可以真正成為函數式程式語言。如果這是最終的結果,那麼Swift可能會成為一門出人意料的重要語言,它可以改進編程規程,因為程式設計師必須學習函數式編程才能在一個非常流行的平臺上開發。學習函數式編程可以讓你成為一個更好的程式設計師,即使你使用其他範式工作。
或者這些都不會發生,Swift可能只是一種編寫非常出色的iOS和Mac應用程式的語言,但這也沒關係。
我相信所有這些東西在Swift中實現都會非常簡單。但是他們並沒有跳出文檔的框框,甚至在任何Swift視頻中都沒有討論過這些內容。這是一種對什麼是重要的和什麼是可能的一種衡量。它們會告訴你所用的範式。我在Scala中進行反應式UI編程的經驗好壞參半。即使有函數式編程的背景,我發現我的學習曲線仍然很高,並且程序很難調試。但這可能只是需要更多的經驗。這似乎是個好主意,我只是不知道它是否真的是個好主意。我以前騎摩託車。當你騎摩託車的時候,你必須比開車的時候更加清醒。否則,你會受傷的。但是我注意到的是,騎摩託車使我成為了更好的汽車駕駛員。函數式編程就是這樣。騎摩託車不一定是到達目的地的最佳方式,但是如果每個人都學會了騎摩託車,我們會擁有更好的駕駛員。如果大學先教Haskell,然後再教Java,我們就會有更好的程式設計師。
原文連結:https://robnapier.net/swift-is-not-functional
本文為CSDN翻譯文章,轉載請註明出處。
【END】