误解!Swift并非多范式函数式编程语言

发表时间: 2020-04-10 20:13

来源 | Cocoaphony

译者 | 苏本如,责编 | 夕颜

封图 | CSDN下载自视觉中国

出品 | CSDN(ID:CSDNnews)

自从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应用程序的语言,但这也没关系。

  1. 我相信所有这些东西在Swift中实现都会非常简单。但是他们并没有跳出文档的框框,甚至在任何Swift视频中都没有讨论过这些内容。这是一种对什么是重要的和什么是可能的一种衡量。它们会告诉你所用的范式。

  2. 我在Scala中进行反应式UI编程的经验好坏参半。即使有函数式编程的背景,我发现我的学习曲线仍然很高,并且程序很难调试。但这可能只是需要更多的经验。这似乎是个好主意,我只是不知道它是否真的是个好主意。

  3. 我以前骑摩托车。当你骑摩托车的时候,你必须比开车的时候更加清醒。否则,你会受伤的。但是我注意到的是,骑摩托车使我成为了更好的汽车驾驶员。函数式编程就是这样。骑摩托车不一定是到达目的地的最佳方式,但是如果每个人都学会了骑摩托车,我们会拥有更好的驾驶员。如果大学先教Haskell,然后再教Java,我们就会有更好的程序员。

原文链接:
https://robnapier.net/swift-is-not-functional

本文为CSDN翻译文章,转载请注明出处。

☞拿下 Gartner 容器产品第一,阿里云打赢云原生关键一战!

☞腾讯面试官这样问我二叉树,我刚好都会 | 原力计划

☞斩获GitHub 2000+ Star,阿里云开源的 Alink 机器学习平台如何跑赢双11数据“博弈”?| AI 技术生态论

☞微软为一人收购一公司?破解索尼程序、写黑客小说,看他彪悍的程序人生!

☞机器学习项目模板:ML项目的6个基本步骤

☞IBM、微软、苹果、谷歌、三星……这些区块链中的科技巨头原来已经做了这么多事!

☞资深程序员总结:分析Linux进程的6个方法,我全都告诉你

今日福利:评论区留言入选,可获得价值299元的「2020 AI开发者万人大会」在线直播门票一张。 快来动动手指,写下你想说的话吧。