Hacker News 中文摘要

RSS订阅

从左到右编程 -- Left to Right Programming

文章摘要

文章批评了Python列表推导式的语法设计,认为其在编写过程中缺乏编辑器支持,导致无法有效进行自动补全和类型验证。作者通过逐步编写代码的过程,指出由于变量和方法的声明顺序问题,编辑器无法在编写时提供有用的建议和错误检查,影响了编程体验。

文章总结

从左到右编程:程序应在输入时保持有效

我不喜欢Python的列表推导式,例如:

python text = "apple banana cherry\ndog emu fox" words_on_lines = [line.split() for line in text.splitlines()]

虽然声明式编程很好,但这种语法在编写时缺乏良好的用户体验。编辑器无法在你输入时提供帮助。让我们逐步分析这段代码的输入过程:

  1. 输入 words_on_lines = [l 时,理想情况下,编辑器应该能自动补全 line,但由于 line 尚未声明,编辑器无法做到。
  2. 输入 words_on_lines = [line.sp 时,编辑器知道你想访问 line 的某个属性,但由于不知道 line 的类型,无法提供有用的建议。
  3. 输入 words_on_lines = [line.split() for line in 时,我们才知道 line 是迭代变量,但 split() 是否是 line 的方法?谁也不知道!
  4. 最后,输入 words_on_lines = [line.split() for line in text.splitlines()] 时,我们才确定 line 的类型,并验证 split() 的调用。由于 text 已经声明,编辑器能够自动补全 splitlines()

这个过程非常不友好。如果你不知道 split() 函数的名称,并希望编辑器提供帮助,你必须先写 words_on_lines = [_ for line in text.splitlines()],然后再回到 _ 处获取 line.sp 的自动补全。

相比之下,Rust 的例子做得更好:

rust let text = "apple banana cherry\ndog emu fox"; let words_on_lines = text.lines().map(|line| line.split_whitespace());

在 Rust 中,程序从左到右构建。第一次输入 line 时,变量就被声明了。一旦输入 line.,编辑器就能提供可能的方法建议。这种体验更加愉快,因为程序在输入时始终处于有效状态,编辑器能够引导你走向成功。

设计中有个原则叫“渐进式披露”:用户应该只接触到完成任务所需的复杂性,且复杂性应在相关时自然浮现。例如,在 Word 中,你不应该在开始输入前选择字体和大小,而添加图片时,文本环绕选项才会出现。

在 C 语言中,结构体不能有方法,因此任何本可以是 myStruct.function(args) 的函数都必须写成 function(myStruct, args)。假设你有一个 FILE *file,你想获取其内容。理想情况下,你应该能输入 file. 并看到所有与文件相关的函数列表,然后选择 read 并继续。但在 C 中,你必须知道与 FILE * 相关的函数通常以 f 开头,输入 f 时,编辑器只能显示所有以 f 开头的函数,最终你可能会找到 fread,但你无法确定这是最佳选择。

在更理想的语言中,你会在输入 file.read 时看到 close 方法的存在,这提示你在完成后需要关闭文件。你自然地在相关时获得了这些信息。而在 C 中,你必须提前知道 fclose 是你在完成后需要调用的函数。

C 不是唯一有这种问题的语言。Python 也有很多例子。例如:

python text = "lorem ipsum dolor sit amet" word_lengths = map(len, text.split())

在 Python 中,函数不可发现。你不知道字符串长度是 lenlengthsizecountnum 还是 #。你只有在尝试所有可能后才能知道。

在 JavaScript 中:

javascript text = "lorem ipsum dolor sit amet"; wordLengths = text.split(" ").map(word => word.length);

当你输入 word.l 时,你会立即看到 length。函数的命名猜测更少,map 也是如此。当你输入 .map 时,你知道这个函数将处理你拥有的数据,不会因为 map 函数期望其他类型或语言中这个函数叫 select 而出现奇怪的错误。

随着逻辑复杂度的增加,Python 代码的可读性会变得更差。例如:

python len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs)))

你必须在行的开头和结尾之间来回跳转才能理解发生了什么。

在 JavaScript 中:

javascript diffs.filter(line => line.every(x => Math.abs(x) >= 1 && Math.abs(x) <= 3) && (line.every(x => x > 0) || line.every(x => x < 0))).length;

逻辑从左到右阅读,更加清晰。

所有这些例子都说明了一个共同原则:程序应在输入时保持有效。当你输入 text 时,程序是有效的;当你输入 text.split(" ") 时,程序是有效的;当你输入 text.split(" ").map(word => word.length) 时,程序是有效的。由于程序在构建时始终有效,编辑器能够帮助你。如果你有一个 REPL,你甚至可以在输入时看到结果。

设计好的 API!

评论总结

评论主要围绕编程语言的语法、工具支持以及代码编写体验展开,观点多样且涉及多个编程语言。以下是总结:

  1. 工具支持与自动补全

    • 一些评论者认为现代工具(如LLM的自动补全)可以显著提升编程体验,尽管某些编程风格可能令人不适。
      • "I can’t argue against how good autocomplete from these LLMs can be."(评论1)
      • "Yeah, having LSP autocomplete here does feel nice."(评论5)
  2. 代码的有效性与编写顺序

    • 有观点认为代码在编写时应保持“有效”状态,但现实中开发者常常跳跃式编写代码,导致代码在编写过程中不完全有效。
      • "Programs should be valid as they are typed."(评论4)
      • "But the reality is that we often jump around, filling in some things while leaving other things unfinished."(评论4)
  3. 编程语言的语法与可读性

    • 不同语言的语法设计对代码的可读性和编写体验有显著影响。例如,Python的列表推导式被认为在某些情况下难以阅读,而Rust的链式调用则被认为更直观。
      • "But it also makes the code harder to scan than Python."(评论5)
      • "The author would probably suggest rust’s syntax of values.iter().split(None).map(Iterator::sum).max().unwrap_or(0)."(评论25)
  4. 管道操作符与函数调用

    • 多个评论者提到管道操作符(如F#的|>)在函数调用中的优势,认为它使代码更自然和易读。
      • "I miss the F# pipe operator in other languages."(评论6)
      • "The consensus here seems to be that Python is missing a pipe operator."(评论24)
  5. SQL与查询顺序

    • SQL的查询顺序(如SELECTFROM之前)被认为不够直观,有评论者建议从FROM开始编写查询。
      • "Queries should start by the FROM clause."(评论15)
      • "SQL shows it’s age by having exactly the same problem."(评论15)
  6. IDE与代码模板

    • 一些IDE通过代码模板和结构化编辑帮助开发者编写有效代码,但并非所有开发者都依赖这些工具。
      • "Some IDEs provide code templates, where you type some abbreviation that expands into a corresponding code construct."(评论16)
      • "Not every developer embraces LSP, for example, as some are thwarted by its opinionated implementation."(评论17)
  7. 语言设计与用户体验

    • 评论者对不同语言的设计提出了批评和建议,认为某些语言在多人协作或复杂场景下表现不佳。
      • "Don’t know why python gets so much love. It’s a painful language as soon as more than one person is involved."(评论10)
      • "Languages with uniform function call syntax like Nim or D do this better."(评论11)

总结中,评论者普遍关注工具支持、语法设计和代码编写体验,不同语言在这些方面的优劣引发了广泛讨论。