响应式编程 函数式编程
根据您要求的对象, 函数式编程 (FP)是一种应运而生的开明编程方法,或者是一种在实践中几乎没有实际好处的过于学术化的方法。 在本文中,我将解释什么是函数式编程,探讨其好处,并推荐用于学习函数式编程的资源。
语法底漆
本文中的代码示例使用Haskell编程语言。 对于本文,您需要了解的只是基本的函数语法:
这定义了一个名为even的单参数函数。 第一行是类型声明 ,它声明甚至接受一个Int并返回一个Bool 。 该实现遵循一个并包含一个或多个方程 。 我们将忽略实现(名称和类型足以说明问题):
在此示例中, map是一个带有两个参数的函数:
- (a-> b) :将a变成b的 a函数
- [A]: 一个列表
并返回b的列表。 再次,我们不在乎定义-类型更有趣! a和b是可以代表任何类型的类型变量 。 在下面的表达式中, a是Int , b是Bool :
map even [ 1 , 2 , 3 ]
评估为[Bool] :
[ False,True,False ]
如果看到其他您不理解的语法,请不要惊慌。 完全理解语法不是必需的。
关于函数式编程的神话
让我们从消除常见的误解开始:
- 函数式编程不是命令性或面向对象编程的竞争对手或对立面。 这是错误的二分法。
- 函数式编程不仅是学者的领域。 的确,函数式编程的历史扎根于学术界,诸如Haskell和OCaml之类的语言是流行的研究语言。 但是如今,许多公司都将函数式编程用于大型系统,小型专用程序以及两者之间的所有内容。 甚至每年都有一次针对函数式编程商业用户的会议; 过去的程序可以洞察功能编程在行业中的使用方式以及由谁使用。
- 函数式编程与monad无关,也与任何其他特定抽象无关。 对于围绕该主题的所有麻烦,monad只是法律的抽象。 有些东西是单子,有些不是。
- 函数式编程并不是特别难学。 某些语言可能具有与您已经知道的语法或评估语义不同的语法,但是这些差异是肤浅的。 函数式编程中有密集的概念,但是其他方法也是如此。
什么是函数式编程?
从本质上讲,函数式编程就是使用函数( 纯数学函数)进行编程。 函数的结果仅取决于参数,并且没有副作用,例如I / O或状态突变。 通过将功能组合在一起来构建程序。 组合功能的一种方法是功能组合 :
( .
)
::
( b
-> c
)
->
( a
-> b
)
->
( a
-> c
)
( g . f
) x
= g
( f x
)
该infix函数将两个函数组合为一个,将g应用于f的输出。 我们将在接下来的示例中看到它。 为了进行比较,Python中的相同函数如下所示:
def compose
( g
, f
) :
return
lambda x: g
( f
( x
)
)
函数式编程的优点在于,因为函数是确定性的并且没有副作用,所以您始终可以用函数的结果替换函数应用程序。 用equals代替equals可以进行方程式推理 。 每个程序员都必须对自己的代码和其他人进行推理,而方程式推理是实现此目的的绝佳工具。 让我们来看一个例子。 您遇到以下表达式:
map even . map ( + 1 )
这个程序做什么? 可以简化吗? 方程式推理使您可以通过一系列替换来分析代码:
我们可以使用方程式推理来理解程序并优化可读性。 Haskell编译器使用方程式推理来执行多种程序优化。 没有纯函数,方程式推理要么不可能,要么需要程序员的不懈努力。
功能编程语言
您需要一种编程语言才能进行功能编程?
在没有高阶函数 (能够将函数作为参数和返回函数传递), lambda (匿名函数)和泛型的语言中有意义地进行函数编程是很困难的。 大多数现代语言都具有这些功能,但是不同语言对功能编程的支持程度有所不同。 获得最佳支持的语言称为功能编程语言 。 其中包括静态类型的Haskell , OCaml , F#和Scala ,以及动态类型的Erlang和Clojure 。
即使在函数式语言之间,利用函数式编程的距离也存在很大差异。 使用类型系统有很多帮助,特别是如果它支持类型推断 (因此您不必总是键入类型)。 本文没有余地进行详细介绍,但是可以肯定地说,并非所有类型系统都是一样的。
与所有语言一样,不同的功能语言强调不同的概念,技术或用例。 选择语言时,考虑它对函数式编程的支持程度以及它是否适合您的用例非常重要。 如果您坚持使用某些非FP语言,则仍可以在该语言支持的范围内应用功能编程来受益。
不要打开那扇陷阱门!
回想一下,函数的结果仅取决于其输入。 las,几乎所有编程语言都具有打破这一假设的“功能”。 空值,类型case( instanceof ),类型转换,异常,副作用和无限递归的可能性是陷阱门,它们破坏了方程式推理并削弱了程序员对程序的行为或正确性进行推理的能力。 ( 总计语言 ,没有任何陷阱门,包括Agda,Idris和Coq。)
幸运的是,作为程序员,我们可以选择避免这些陷阱,并且如果我们受到纪律处分,我们可以假装陷阱门不存在。 这个想法称为快速和宽松推理 。 它不花任何钱-几乎无需使用陷阱门就可以编写任何程序-避免它们,您将赢得等式推理,可组合性和重用性。
让我们详细讨论异常。 该活板门破坏了公式推理,因为这种类型未反映异常终止的可能性。 (如果文档甚至提到了可能引发的异常,请给自己一个幸运的机会。)但是没有理由不能让我们拥有一个包含所有故障模式的返回类型。
避免陷井门是一个在其中语言功能可以发挥很大作用的领域。 为了避免异常,可以使用代数数据类型对错误条件进行建模,如下所示:
-- new data type for results of computations that can fail
--
data Result e a
= Error e
| Success a
-- new data type for three kinds of arithmetic errors
--
data ArithError
= DivByZero
| Overflow
| Underflow
-- integer division, accounting for divide-by-zero
--
safeDiv
::
Int
->
Int
-> Result ArithError
Int
safeDiv x y
=
if y
==
0
then Error DivByZero
else Success
(
div x y
)
在此示例中的权衡是,您现在必须使用Result ArithError Int类型的值,而不是普通的旧Int ,但是有一些处理此问题的抽象方法。 您不再需要处理异常,并且可以使用快速和宽松的推理,因此总的来说,这是双赢。
免费定理
大多数现代的静态类型语言都具有泛型 (也称为参数多态 ),其中函数是在一种或多种抽象类型上定义的。 例如,考虑一个列表中的函数:
f
::
[ a
]
->
[ a
]
f
= ...
Java中的相同函数如下所示:
static < A > List < A > f ( List < A > xs ) { ... }
编译后的程序证明了该函数可以与类型a的 任何选择一起使用 。 考虑到这一点,并采用快速和宽松的推理,您能算出函数的作用吗? 知道类型有帮助吗?
在这种情况下,类型不能确切告诉我们函数的功能(它可以反转列表,删除第一个元素或其他很多东西),但是可以告诉我们很多东西。 只需从类型中,我们就可以得出有关函数的定理:
- 定理1 :输出中的每个元素都出现在输入中; 它不可能将a添加到列表中,因为它不知道a是什么或如何构造a。
- 定理2 :如果在列表上映射任何函数然后应用f ,则结果与应用f然后映射相同。
定理1可帮助我们了解代码在做什么,定理2可用于程序优化。 我们只是从类型中学到了所有这些! 这种结果(从类型中得出有用定理的能力)称为参数性 。 因此,类型是函数行为的部分(有时是完整的)规范,是一种经过机器检查的文档。
现在该轮到利用参数化了。 您可以从map和(。)的类型或以下函数得出什么结论?
- foo :: a->(a,a)
- 栏:: a-> a-> a
- baz :: b-> a-> a
学习函数式编程的资源
也许您已经确信函数式编程是编写软件的更好方法,并且您想知道如何入门? 有几种学习函数式编程的方法。 这是我推荐的一些(我承认,对Haskell有强烈的偏见):
- UPenn的CIS 194:Haskell简介对函数式编程概念和实际的Haskell开发进行了扎实的介绍。 课程材料可用,但讲座不可用(您可以查看几年前布里斯班功能编程小组关于CIS 194的系列演讲 )。
- 好的入门书籍包括Scala中的函数式编程, 与Haskell进行函数式思考以及从第一原理开始的Haskell编程 。
- Data61 FP课程 (fka, NICTA课程)通过类型驱动的开发讲授基础抽象和数据结构。 回报是巨大的,但是设计却很难 ,因为它起源于培训工作坊,所以只有在您知道愿意指导您的函数式程序员的情况下,才尝试这样做。
- 开始使用正在处理的任何代码进行函数式编程。 编写纯函数(避免不确定性和变异),使用高阶函数和递归而不是循环,利用参数性提高可读性和重用性。 许多人通过试验和体验各种语言的好处来开始进行函数式编程。
- 加入您所在地区的功能编程用户组或研究组,或开始一个活动组,并注意进行功能编程会议(新的会议一直在弹出)。
结论
在本文中,我讨论了什么是函数编程以及什么不是函数编程,并讨论了函数编程的优点,包括方程式推理和参数化。 我们了解到,您可以使用大多数编程语言进行某些函数式编程,但是语言的选择会影响您受益,因为Haskell等函数式编程语言可以提供最多的功能。 我还推荐了用于学习函数式编程的资源。
函数式编程是一个广阔的领域,还有许多更深入(更密集)的主题正在等待探索。 我不愿提及一些具有实际意义的东西,例如:
- 镜头和棱镜(一流的,可组合的吸气剂和吸气剂;非常适合处理嵌套数据);
- 定理证明(为什么您可以在证明自己正确的情况下测试代码?);
- 惰性评估(让您使用可能无限的数据结构);
- 和类别理论(函数式编程中许多美丽而实用的抽象的起源)。
我希望您喜欢函数式编程的入门知识,并能从中受益于这种有趣而实用的软件开发方法。
本文是根据CC BY 4.0许可证发布的。
翻译自: https://opensource.com/article/17/4/introduction-functional-programming
响应式编程 函数式编程