用数组语言思考(2022)

像 K、APL、J 和 BQN 这样的数组编程语言承诺极其简洁、带有数学风格的代码,以及对整个数组进行强大的组合处理,但许多程序员质疑,与主流语言中的现代函数式特性(如 map/filter/reduce)相比,这种做法是否真的更划算。评论者权衡了这些语言的优点——语义密度、更快的重构、更清晰的高层模式,以及通过 SIMD 和向量化获得的良好性能——与其陡峭的学习曲线、不同寻常的符号语法、对临时数组的重度使用,以及对长期可维护性和团队采用的担忧。也有人指出,即便这些语言仍属小众,学习“用数组思考”也会改变你在 NumPy、R 或 Haskell 等更传统环境中组织数据和循环的方式。

资源与生态系统

  • 若干评论链接到用于学习数组语言(APL、K、J、BQN)的教程、播客和聊天室。
  • 有一些例子展示了嵌入到其他语言中的类数组系统(例如 APL-on-Lisp、基于 Python 托管的 KlongPy)。
  • Numpy、R、Matlab/Octave 和 PyTorch 被视为“数组框架”,而不是完整的数组语言。

简洁性、记法与可读性

  • 支持者认为,简洁性带来了“语义密度”:一屏内承载更多逻辑、更少间接层次、更容易重构,以及更快的模式识别。
  • 据说简洁的代码会让 bug 更加显眼,并能实现“无畏重构”,尤其是在 REPL 中。
  • 批评者则认为,符号密集的代码像“符号汤”,难以阅读和维护,而且需要在极小的表达式旁配上很长的解释。
  • 有人担心这种风格会鼓励炫技而不是长期可维护性,并会让新手望而却步。

与 FP 和主流语言的比较

  • 数组语言所宣称的许多优势(map/filter/reduce 风格、组合、隐式编程)在 Haskell、F# 和 Clojure 等函数式语言中也存在。
  • 一些评论者表示,一旦他们学会了 point-free FP,J/K/APL 就不再那么有吸引力了。
  • 另一些人强调,数组语言统一了所有秩和形状上的操作,并且高度复用一小组可组合的原语,这与典型库不同。

性能、内存与临时数组

  • 一个关键争论是:数组语言往往会物化大量中间数组。担忧在于,对于“用谓词 P 过滤小于 N 的数字”之类的问题,会浪费内存和带宽。
  • 回应包括:
    • 内存通常很便宜;当它不便宜时,用户可以手动分块计算(按块处理)。
    • 一些实现使用惰性、循环融合、习语识别以及对临时数组的原地复用。
    • 数组风格与 SIMD 和并行硬件非常契合;即便存在临时数组,它也可能优于未向量化的标量循环。
    • 批评者反驳说,缓存行为和带宽仍然重要,而天真的物化在性能上可能输给流式标量代码。

类型系统与维度正确性

  • 人们对能够表达数组秩和形状的类型系统很感兴趣(例如,确保矩阵维度对齐、安全的广播)。
  • 讨论提到了依赖类型/形状类型和研究性语言,但目前系统在多大程度上能捕捉数组语言算子的完整一般性仍不清楚。

用例、学习曲线与采用情况

  • 数组语言被认为对数值计算、线性代数和数据并行任务极其强大,但“并不适合每个问题”。
  • 一些人觉得它们能极大拓展思维,并表示数组思维改善了他们在其他语言中的编码方式。
  • 另一些人则认为“万物皆数组”的偏向使某些问题比在数据结构更丰富的语言中更难处理。
  • 对于在 APL/J/K 中构建大型、多成员、长寿命代码库,大家持怀疑态度;这类场景下的可维护性实践在该线程中大多并不明确。