"一个人可以抵御军队的入侵;一个人却不能抵御思想的入侵"。- 维克多-雨果
近来,函数式编程已经入侵了世界各地的代码库。
看看那些主要的编程语言吧。如果没有高阶函数、那些新开发者往往极度过度使用的匿名函数和其他可爱的东西,我们就无法生存。
这不是一个功能性编程教程,它将展示如何在JavaScript中使用这些特性。你可以在freeCodeCamp上找到。
在这个只能被描述为 "一个疯狂的举动,它将削弱这篇文章的浏览量 "的地方,我更愿意谈论这些功能所来自的范式。准备好了吗?
什么是函数式编程?
简而言之,函数式编程是一个包罗万象的术语,指的是一种写代码的方式,专注于组成纯函数,实际使用过去几十年来类型系统的创新,以及总体上是很好的。
那么重点是什么呢?所有这些东西都有助于更好地理解我们的代码中实际发生了什么。
而且,一旦我们做到这一点,我们就会获得:
你是一个函数式程序员,哈利。
事实上,函数式编程是为分布式系统和复杂的后端开发代码的理想选择,但这并不是它能做的全部。在Serokell,我们把它用于大部分的行业项目。无论你需要的是前端还是后端,这并不重要,现在什么都有FP语言。
现在,你对学习更多的函数式编程感到兴奋,并且已经在亚马逊上订购了《Haskell编程》的副本,让我们深入了解一下细节。
90年来,从lambda微积分到lambda标识
函数式编程的核心是lambda微积分。
由数学家Alonzo Church在20世纪30年代提出,lambda微积分只是表达我们如何计算某些东西的一种方式。如果你理解了这个,你将获得很多关于函数式编程在实践中的直观感受。
在lambda微积分中只有三个元素:变量、函数和将函数应用于变量。在这里,我们必须把函数看作是一个纯粹的/数学上的函数:一种把一组输入的成员映射到一组输出的成员的方式。

尽管它是一个非常简单的工具,但我们实际上可以组成不同的函数,并以这种方式来编码任何可能用普通计算机进行的计算。(不过对于任何非琐碎的事情来说,它都会很快变得笨重,这就是为什么我们不在它里面编程的原因)。

为了进一步说明这个概念,我推荐你看这个视频,一个绝对的疯子在Python中实现了lambda计算。
在1950-60年代,人们开始将这个概念编码到编程语言中。一个很好的例子是LISP,一种由John McCarthy设计的函数式语言,它保持了lambda calculus的整体不可理解性,同时实际上使你能做一些事情。
Racket(LISP的一种方言)中A*搜索算法的实现实例:
#lang racket
(require racket/set)
(define (pq-inserts elems pq)
(sort (append elems pq) (lambda (a b)
((first a) . < . (first b)))))
(define (a-star estimate neighbors dist eps start)
(define (go visited pq)
(match pq
[(list* (list estimation distance path) rest-pq)
(let ((point (first path)))
(if ((estimate point) . <= . eps)
path
(let*
( (near
(filter
(compose not (curry set-member? visited))
(neighbors point)))
(paths
(for/list ([pt near])
(let ((distance1 (+ distance (dist point pt))))
(list
(+ distance1 (estimate pt))
distance1
(list* pt path))))))
(go
(set-add visited point)
(pq-inserts paths rest-pq)))) )]
[else
'()]))
(define initial
(list (list 0 0 (list start))))
(reverse
(go
(set)
initial)))
但这仅仅是个开始。一件事导致了另一件事,当我们引入ML和Miranda这样的语言时,无数的变体探索增加了可读性和一个伟大的类型系统。结果,20世纪80年代出现了一些美丽的东西--Haskell,这种编程语言如此伟大,以至于在接下来的30年里注定要躲避主流。
同样的A*算法在Haskell中:
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Set as Set
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty (..), (<|))
{- |
I use `Map.Map` as a priority queue, by popping element
with a minimal key using `Map.minView`.
-}
aStar
:: (Num d, Ord d, Ord a)
=> (a -> d) -- distance estimation
-> (a -> [a]) -- neighbours retrieval
-> (a -> a -> d) -- distance between 2 points
-> d -- distance from end we should reach
-> a -- starting point
-> Maybe (NE.NonEmpty a)
aStar estimate neighbors dist epsilon start = do
NE.reverse <$> go Set.empty initialQueue
where
initialQueue = Map.singleton (cost start 0) (start :| [], 0)
cost point traversed = estimate point + traversed
go visited queue = do
((path @ (point :| _), distance), queue') <- Map.minView queue
if estimate point <= epsilon
then do
return path
else do
let near = filter (`Set.notMember` visited) (neighbors point)
batch = flip map near $ \point' ->
let distance' = distance + dist point' point
in (cost point' distance', (point' <| path, distance'))
go (Set.insert point visited) (Map.fromList batch <> queue')
我们稍后将回到Haskell。
还有什么?
好吧,我希望我给出了关于纯函数和链式纯函数的直观印象。还有什么呢?
-
不变性:这是从纯函数中得出的。如果函数有一个输入并给出一个输出,并且不保持任何状态,就不可能有可变的数据结构。忘掉i++吧。这是为了更好的发展。可变数据结构是一把悬在开发者头上的剑,随时都有可能掉下来。
当底层代码需要是线程安全的时候,不可变性也有帮助,因此在编写并发/并行代码时是一个巨大的福音。
-
各种各样的处理函数的方法:匿名函数、部分应用函数和高阶函数--这些你都可以在所有现代编程语言中得到。主要的好处是当我们在抽象的阶梯上走得更高时。我们可以引入各种设计模式,如漏斗、单体,以及我们从范畴理论中移植的任何种类的变形,这是数学中最强大的工具之一,因为......明白吗?我们的代码是一个数学函数的组合。
你有可能在看到不变性的时候停下来想:如果不保持一个全局状态,我们怎么能完成任何事情?这不是非常尴尬吗?不是的。我们只是通过函数传递相关的状态。
虽然一开始看起来很不方便(这更多是因为这是一种新的编程风格,而不是因为内在的复杂性),但函数式编程的抽象帮助我们很容易做到这一点,例如,我们可以使用特殊的结构,如状态单体,在函数之间传递状态。
正如你所看到的,函数式编程的概念可以很好地相互协同。最后,我们有一个自洽的范式,对于任何你想包含其中一个元素的地方都是非常好的。
函数式编程语言将使你的业务丰富得令人难以置信
不过,我一直在隐瞒最伟大的事情。
你知道现在有很多聪明人在做Haskell & Co吗?功能性编程是收集/满足尚未被FAANG的企业魔掌吞噬的人才的好方法。
我们从经验中知道这一点。我们的工程师都是坏蛋,而且不仅仅是在我们的团队页面上。
所以,如果你想启动一个项目,并且你想和一个能让你震惊的团队一起启动它,我将列出一些功能型编程语言,用它们来吸引下一代的开发者。
哈斯克尔

Haskell是在很远很远的年代开发的,当时FP社区面临着有太多该死的具有类似属性的函数式编程语言的情况。事实证明,当你把许多聪明人聚集在一起时,会发生一些事情。但关于这一点,请看我们的Haskell历史文章。
从那时起,Haskell已经在某些领域建立了自己的地位,例如:
- 金融
- 生物技术
- 区块链
- 编译器和DSLs
许多大公司都有不同规模的项目使用Haskell。
Haskell是各种想法的组合,它们汇集在一起,创造了一种完全的(表达)能力:
- 纯粹性: 在纯洁的代码(由纯洁的函数组成)和不纯洁的代码(输入/输出)之间有一个明确的界限。
- 静态类型化:类型是在编译时检查的,而不是在运行时。这就避免了很多运行时的崩溃,以换取实际处理类型,有些人觉得这很难。
- 懒惰性:表达式只有在需要表达式的值时才会被评估,这与严格的评估不同,在严格的评估中,表达式被绑定到变量时才会被评估。
- 不变性: 数据结构是不可改变的。
它是我们最喜欢的语言之一,这是有原因的。Haskell,如果使用得当,可以提供。它所提供的是精确而有效的代码,易于维护。
斯卡拉

想使用功能语言,但又想在这里和那里用几个类来糟蹋它?
Scala就是这样的选择。由于某种原因,Scala受到编写Apache Spark的人的青睐,它对大数据处理、服务和其他功能化编程令人惊叹的地方非常有用。
Scala的另一个好处是,它可以编译成JVM。如果这是你作为一个管理者需要将函数式编程引入到Java代码库中的东西,那你就去吧!
一旦你开始编写不与JVM交互的纯函数式Scala,就没有太多的理由直接切换到Haskell,因为它的支持要好得多。
OCaml

如果说Haskell有点小众,那么OCaml则是超级小众,它的主要支撑点之一是法国当地的开发者支持。
但也许现在不是了。例如,与列出的其他编程语言类似,它已经看到在区块链中的应用,特别是Tezos。而且他们有自己的理由。
OCaml是那些模糊了函数式编程和面向对象语言之间界限的语言之一。因此,对于一个新的函数式程序员来说,使用OCaml而不是Haskell可能更直观。OCaml不那么执着于纯洁性,用它写东西的人也比较实际:如果你只是想在OCaml中振翅高飞,你可能会在其他开发者的攻击下幸存下来。
Elixir

你知道世界上最好的网络框架是用一种函数式编程语言写的吗?生产力强。可靠的。快速。是的。
Elixir是一种功能性的、通用的编程语言,在BEAM(Erlang虚拟机)上运行。它因在创建低延迟和容错的分布式系统中的作用而闻名。此外,它很擅长创建根据网络需求进行扩展的东西。Elixir在WhatsApp和Netflix这样处理大量数据并需要快速处理的公司得到了极好的应用。如果你正在做类似的事情,你不能错过这个。
为你的项目提供Serokell
你知道的,我不能不在最后介绍一下。函数式编程对于广泛的系统和结构来说是非常好的。然而,不是每个企业都能投入足够的资源来执行如此复杂的工作。Serokell理解这种挣扎,旨在提供最好的服务,以确保贵公司的编程项目顺利和可靠。
我们的开发人员团队提供不同语言的开发服务。我们不仅编写代码,而且还执行项目,从其开始的想法到最后的阶段。这意味着我们也可以为您做研究、设计和其他相关服务。虽然我们提供了一个多功能的编码语言范围,但我不得不提醒,我们主要使用Haskell。