你是个困惑的程序员吗?学习一门函数式语言

2025-06-07

你是个困惑的程序员吗?学习一门函数式语言

替代文本

坦白说:我是个容易犯糊涂的程序员。我的思维不像白板天才。我天生是个艺术家,最擅长涂鸦。我自学了可视化语言编程,因为我偏爱它创造的图形和键盘的敲击声。与其说我是程序员,不如说我更像个艺术家和作家。阅读复杂代码时我很容易感到困惑,所以如果可能的话,我几乎总是更喜欢阅读人性化的文档和注释,而不是阅读源代码。

自从Recurse Center之后,过去几年我都在程序员休假,四处游荡,尝试各种有趣的编程语言。这就像一段寻找“雷神之锤”的个人旅程。有些语言我坚持了很久,而有些我几乎一接触就放弃了。因为每种语言的设计都针对不同的思维方式,我知道我必须不断寻找最适合我的工具。

多年来,甚至在寻找工作机会之前,我就用过 C++、Java、Python 和 Go。在这些语言中,Go 最适合我,它能让我以最快、最自然的方式工作。当时我的巅峰状态是,只用了一个多月就写出了我的第一个正式的开源库。然而,我很快意识到它对我来说仍然不够(我继续使用 Go,因为它极大地提高了工作效率),所以当我被 Recurse Center 录取​​后,我决定涉足函数式编程这个未知领域。我最初学习的是 Lisp 和 Clojure,后来又学习了 Ocaml 和 Haskell,虽然这些语言令人望而生畏,但却让我耳目一新。还没等我走远,我就在硅谷找到了一份工作,不得不搬到美国海岸的另一边,在接下来的三年里用 JavaScript 和 Python 写代码。

我花了好几年才意识到,作为一个困惑的程序员,我并非个例。大多数程序员都是这样。我们得承认,我们大多数人并非天生就擅长编程,这应该没问题,因为编程是一个学习的过程,而不是后天习得的天赋。在我担任工程师的这些年里,我的 JavaScript 和 Python 代码(JavaScript 代码中尤其如此)犯了无数 bug,这主要是因为我所在公司的节奏很快,缺乏指导、最佳实践以及对单元测试的重视,但更主要的是因为我当时感到困惑。我倾向于在不必要的情况下抽象化某些东西,因为抽象能让我思考代码时不那么困惑。当我面临最后期限时,压力很大,我只能像掷骰子一样写代码——盲目地尝试代码,反复运行,直到代码运行正常。因为 Python 和 JavaScript 允许我运行代码,即使它不太可能是正确的,所以“交付它”的创业心态变成了“f*** it,让我们提交”(我猜工程师甚至被鼓励以快速行动和破坏事物的方式在用户面前推送代码并修复它)。

快进到现在,我作为唯一的创始人,一边打造自己的产品,一边经营自己的公司。这既让人放松,又让人紧张。放松是因为现在我不用再遵循别人朝九晚五的规则,不用通勤(新冠疫情封锁期间,很多人也一样),可以学习任何我想学的东西。

在构建核心产品——一个用 Go 和 JavaScript 编写的视频结账平台的同时,我还专门留出了 10% 的时间来学习 Crystal、Rust 和 Haskell 等新语言。这些语言我都尝试过不止一次,但一直没有时间(或经验)坚持下去。

我几乎立刻就放弃了 Crystal,因为它的语法太像 Ruby(这也是为什么我没能选择 Elixir,尽管我自己用 Erlang 效率很高)。我喜欢 Rust,但我知道,在我获得任何投资回报之前,它就会妨碍我的效率提升。后来 Haskell 让我豁然开朗。多年前,我第一次尝试学习 Haskell 时,它对我来说还很神秘,但多年来用 Ocaml、Erlang 和 Lisp 编程的经历,让我的技能提升到现在开始喜欢它的地步。

函数式语言(尤其是 Haskell)一直被贴上“只适合学者和学术项目”(例如编写编译器)的标签,臭名昭著。它们也因此背负了“高深莫测”的恶名。再次强调,人性就是喜欢宣扬自己的喜好,却对自身不满之处避而不谈。大多数不理解函数式编程的人都喜欢喧嚣,而且就像任何负面新闻一样,他们的传播速度更快。

在我看来,像 Ruby、Python、Java 和 Go 这样的命令式和面向对象语言,对于像我这样容易困惑的程序员来说,简直是眼花缭乱。它们实在太让我困惑了,我通常需要一个真正智能的 IDE 来提高效率。例如,在我的 Go 代码库中,我需要到处寻找变量和对象的引用。这感觉就像一部包含很多集数的长篇电影,一旦你停下来再回来,就需要再看一遍才能跟上剧情。

在函数式编程中,每一段代码都是独立的。由于某个表达式的抽象程度较高,理解它的作用可能比较困难,但一旦理解了,它就变得不可更改了。函数式语言中很少有让我感到困惑的“魔法”,而且由于大多数状态都是不可变的,我不想为了确保某些东西能够持久化而创建新的细节。

例如,如果说函数式编程只有一个特性,我只能选择所有语言都具备,那就是模式匹配。它能让程序员直观地看到他们正在处理的数据。


{- 
 - `h:t` reads `h` cons `t`
 - which means it is a list 
 - of at least an element
 - in front and the rest.
 -}

-- Get the first element in the list.
firstElement (h:t) = Just h
firstElement []    = Nothing


-- Sum all elements in the list.
sumList (h:t) = h + sumList t
sumList []    = 0

Enter fullscreen mode Exit fullscreen mode

在 Go(我目前最有成效的语言)中,相当于:


// Get the first element in the list.
func firstElement(list []interface{}) interface{} {
    if len(list) == 0 {
        return nil
    }

    return list[0]
}

// Sum all int elements in the list.
func sumListOfInt(nums []int) int {
    sum := 0
    for _, num := range nums {
        sum += num
    }

        return sum
}

Enter fullscreen mode Exit fullscreen mode

Go 版本涉及的内容更多,尤其是当我阅读的时候。

这里需要注意 Go 中类型的使用interface{},以及特意添加的sumListOfIntexample 函数。它体现了 Go 过于简单的设计与 Haskell 复杂的类型系统相比的弱点,由于与主题无关,这里就不讨论了。

我无意挑起另一场论战,因此我只是将 Haskell 与我正在使用且深耕的语言进行比较。我只是想说,与人们的认知相反,函数式编程实际上更容易让普通程序员理解。它读起来更接近英语指令,因为它不需要程序员成为计算机的寄存器。为了更清楚地说明这种区别,我们来看一个现实世界的任务,以及如何用两种不同的方式完成它:

去杂货店买意大利面、番茄酱和香肠。

至关重要的

从您所在的位置步行20码,直到看到一个路口,右转,继续行驶0.5英里。当您看到右侧有一所学校时,继续行驶10码,然后穿过第一个十字路口到街对面。再步行两个街区即可到达商店。

当您在商店时,请在 3 号过道的尽头找到意大利面,在中间的 4 号过道找到番茄香蒜酱,然后前往冰箱去拿香肠。

...

功能

一直走到看到路口,右转,一直走到看到右边的一所学校,过马路,一直走到看到商店。

对于所有的过道,只拿意大利面和酱汁,然后去冰箱拿香肠。

...

函数式编程帮助程序员避免过于具体,并专注于高级指令(做什么而不是怎么做),因为具体性是人类通常不擅长的(不相信我?你能回忆起在到达路口之前你首先要走多远,而无需回到杂货店模拟吗?)

如果你像我一样,面对一大堆代码就会感到困惑,不妨尝试学习像 Haskell 这样的函数式语言。它或许能完美地映射你的思维模型。


如果您想尝试阅读我对非编程主题的想法,您可以订阅我的新博客 BETA School

Joe Chasinga 时事通讯 BETA 学校

文章来源:https://dev.to/pancy/are-you-a-confused-programmer-learn-a-lingual-23f5
PREV
应对程序员的倦怠
NEXT
Next JS 对构建网站和应用程序的优势