我在使用函数式编程一年中学到的 3 件事
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
和绝大多数编程课程一样,我最初接触编程也是从面向对象编程(OOP)开始的。尽管很多语言都是多范式的,比如Python、C++、JavaScript和Ruby,但OOP仍然是主流。
去年,我在一份新工作中接受了使用 Elixir 技术栈的挑战。这对我来说是一次全新的体验,因为我之前从未接触过函数式编程。学习一种新的编程范式是我职业生涯中最重要的一步,接下来我将分享过去一年来的经验和心得。
1. 学习函数式编程需要重新启动
作为一名面向对象的程序员,在学习一门新语言时,你已经了解了其中的概念,你可能只需要学习语法、如何声明变量、类、公共和私有方法,以及一些特定于该语言的功能。
你知道怎么开车。开另一种车并不难。但函数式编程不仅仅是另一种车,而是一艘船。你不能把所有开车经验都用在航海上。有些经验适用,有些则不适用。
如果你想学习函数式编程,就得从头再学一遍。函数式编程没有共享状态,遵循纯函数式编程的原则,注重关注点分离,并将函数作为主要的运算单元。
在我学习函数式编程的第一个月,我感到很沮丧,因为我试图用面向对象编程的方式解决日常问题,或者说……用我唯一知道的方式解决日常问题。
重启是关键。
2. 副作用是万恶之源
可变性是必要的。开发一成不变的软件毫无意义。关键在于如何处理这些变化。
软件状态越多,维护、理解和调试就越复杂。虽然不可能编写出一个完全纯净且有用的程序(函数式编程中的纯净性指的是没有副作用),但你可以将软件中不纯净的部分(例如数据库、外部 API 调用、I/O 等)隔离出来。
变异是 bug 的来源。因此,避免变异可以减少引入 bug 的数量。每当你修改一个变量时,总有可能破坏了依赖于它的其他代码。避免变异可以防止某些类型的 bug 的出现。——汤姆·达林(来源)
由于没有共享状态,内存完全隔离,这使得并发软件更容易开发和维护。
在函数式编程范式中,由于不可变性是默认结构,并发编程即使在共享状态的情况下也能保证确定性执行。另一方面,命令式编程和面向对象编程使用可变状态,这些状态在多线程环境下难以管理,从而导致程序执行不确定。——Manning (来源)
不仅要避免和隔离副作用,还要避免代码中的可变性。你不需要使用函数式编程语言才能将函数式概念应用到你的代码中。看看这段 Ruby 代码片段:
# Functional-style Ruby
def calculate_clothing_total(items)
items
.select(&:clothing?)
.map(&:amount)
.reduce(0, :+)
end
# Non-functional Ruby
def calculate_clothing_total(items)
total = 0
expenses.each do |e|
total += e.amount if e.clothing?
end
total
end
这个简单的例子只是修改了total变量,但情况可能更糟。在 Ruby 中,要假定值是不可变的,并使用非破坏性方法。尽量不要重新赋值方法参数,因为这可能会导致意想不到的行为。相反,应该返回一个新值。
如果你是一名 Ruby 开发人员,我推荐你阅读 RubyPigeon 上的这篇精彩文章:避免 Mutation – Ruby 中的函数式风格。
3. 关注点分离 = ❤️
有人说,函数式编程软件更容易调试,bug更少,而且一旦掌握了函数式编程,也更容易维护。这确实如此,因为在某些函数式编程语言中,功能分离几乎是强制性的。你肯定不希望纯代码和不纯代码混在一起。
这个概念同样适用于非功能性软件。由于所有部分都得到了很好的隔离,因此更容易进行测试、重构和调试。即使是数据库变更,当持久化和模式变更分离时(就像 Ecto 使用变更集那样),也变得纯粹起来,这是一种处理数据变更的纯粹方法。
attributes = %{amount_due: 3000, due_date: "2019-04-30", scheduled_send_date: "2019-04-25", closed_at: "2019-05-01"}
changeset = Invoice.changeset(%Invoice{}, attributes)
#=> #Ecto.Changeset<
# action: nil,
# changes: %{amount_due: 3000, due_date: ~D[2019-04-30], scheduled_send_date: ~D[2019-04-25]},
# errors: [],
# data: #Invoice<>,
# valid?: true
# >
changeset.changes
#=> %{amount_due: 3000, due_date: ~D[2019-04-30], scheduled_send_date: ~D[2019-04-25]}
如果你想学习函数式编程,我建议你在深入学习 Elixir 之前先掌握函数式编程的基础知识。Elixir 是我在开发 API 等通用软件时首选的函数式编程语言。
如果你已经掌握了基础知识,那就从 Elixir 开始吧,尽情享受吧!🚀
最后但同样重要的是,如果您不打算使用函数式编程语言,我建议您在日常代码中至少应用一些函数式编程的理念,例如隔离性、不可变性以及其他一些函数式编程的优势。有很多库可以帮助您实现这一点,例如JavaScript 的 Immutable.js。
文章来源:https://dev.to/vinibrsl/3-things-i-learned-in-1-year-working-with-functional-programming-3gkj


