编程与软件工程journal.stuffwithstuff.com编辑精选
你的函数是什么颜色?
What Color Is Your Function?
2015 — 2024,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
这篇经典文章用“函数有颜色”的比喻剖析了 async/await 式异步编程的核心缺陷:函数被分成同步(无色)和异步(红色)两类,红色函数会沿着调用链“传染”,迫使整个代码库改写。文章由此解释了为什么 Node 生态会全面倒向回调/异步风格,以及这种二分法给语言设计带来的长期代价。
为什么经久不衰:它精准命名了异步编程中一个至今仍困扰各语言(JS、Python、Rust 等)的结构性问题,每次 async/await 之争兴起时都会被重新引用。
HN 评论精选
函数染色问题本质上是效果系统问题,monad 是比 async/await 更通用的解法
可以把“蓝色”大规模推广为“纯函数”,“红色”推广为“非纯函数”,最终结果本质上就是 Haskell。但仅有(纯/非纯)两色太受限了:蓝色函数行为一致且组合性极好,红色函数却种类繁多——异步、有状态、抛异常、非确定性、CPS、IO 副作用等等。我们真正需要的是一种表示不同种类的“红色”并让它们优雅组合的方式,而这正是 monad 和 monad transformer 所解决的问题。Java/C#/Go 都只引入了固定数量的“红色”和特定的连接方式,而 monad 是可泛化的方案。
tel查看英文原文
We can massively generalize this by calling "blue" "pure" and "red" "impure". The end result is essentially Haskell (but you can take it much further, too!). --- There's something horrifyingly restrictive about having merely (blue -> pure, red -> impure), though. All "blue" functions behave roughly the same (e.g., very, very nicely and compositionally) but "red" functions come in many classes: async, stateful, exception-throwing, non-deterministic, CPS'd, IO-effectful, combinations of the prior. What we want is a nice way of representing different kinds of red functions and how they all interact. What we'd also like is a nice way of composing different kinds of red functions so that the bundled piece has a sensible kind of redness too and we can keep composing. And this is exactly monads and monad transformers. There are other ways to achieve this end as well all under the general name "Effect Typing". Very cool stuff. But what I'd like to emphasize is that Java/C#/Go have not solved this larger problem. They each introduce a fixed number of "rednesses" and very specific ways that different kinds of red can be connected together. Monads are a generalizable solution. The situation is exactly the same as HOFs themselves. We've had subroutines and functions for a long time, but first-order and higher-order functions are a marked increase in power since you can now refer to these "function things" directly. Monads take the notion of "CPS Transform" and allow you to refer to it directly, to mark sections of code which use it and compose them intelligently. They allow you to invent your own kinds of redness on the fly and ensure that your custom colorations compose just as nicely as the built-in ones. If this article is even slightly interesting then you owe it to yourself to learn much more about effect typing. It'll change your world. (That and sum types, because nobody is served by a bunch of integers named Token.LEFT_BRACKET.)
async 本质是倒退回协作式多任务,线程仍是相对最不坏的选择
用过 Node 和 Tornado 之后,我对各种 async 方案都很反感。想想 90 年代初 Windows 和 Mac OS 的协作式多任务:必须显式让出控制权,否则整个系统卡死,后来被抢占式多任务取代,程序员可以放心调用阻塞 IO,由调度器处理。现在的 async 就像协作式多任务的回归,而且更糟:不仅要显式 yield,事件循环还不能好好利用多核,堆栈跟踪也不连贯,极难调试。理论上很快,但只要一个请求忘记 yield 就会堵死全局——用多进程加分发器解决的话,等于糟糕地重新发明了抢占式多任务。线程不完美,但除 STM 和 actor 模型外,它是痛苦最少的方案。
overgard查看英文原文
I'm really out on most of the "async" stuff, after having used it. (Mostly in Node and Tornado) Remember in the early 90s when Windows and Mac OS were "cooperatively" multitasked? Which is to say, you had to explicitly yield to allow other applications to run (or risk locking up the entire system). And then it was replaced with pre-emptive multitasking, which allowed the scheduler to figure out what process deserved CPU time, while allowing the programmer not to have to think about it. You could call a blocking IO function, and the OS would just go do something else while you waited. All this "async" stuff seems like a return of cooperative multitasking, only worse. Not only do I have to explicitly yield, but now it's to some event loop that can't even properly use multiple cores, or keep a coherent stack trace. It's a nightmare to debug. It's theoretically fast... except if one request forgets to yield, it can clog up the entire thing. I guess you use multiple processes for that and a dispatcher, but at that point you've basically reinvented preemptive multitasking... badly. Threads aren't perfect, but excluding STM and actor models they definitely suck the least.
实践解法:把所有函数统一 promise 化,让“染色”不再成为问题
我写 JavaScript 全栈好几年了,采用的做法是把一切都强制转成 promise——用作者的话说就是把一切都染成红色。同步代码混进异步代码也没关系,照样加进 promise 链即可立即 resolve;回调式库就包一层 promise。一切皆 promise 用起来非常好:任何东西随时可能变成异步而无需改变代码流程。额外的好处是错误处理位置可以自选——promise 被拒绝时会跳过链上后续操作直达 catch,你可以在一处统一处理错误,而不是每个操作后面都写一遍 if error。
jenius查看英文原文
So I've been writing javascript full time for a couple years at this point, client, server, and open source, and what I have adopted is coercing everything into promises, which I suppose would be the author's way of saying making everything red. If you have something that is not async mixed in with something that's async, you can still add it to the promise chain and it will resolve right away. If you have a library that uses callbacks or some other thing, you can just wrap it such that it now uses promises. And then of course you can always look for alternate libraries that use promises from the start and skip step as well. I've found that using promises for everything works super well. There is no confusion or doubt at all. Everything has the potential to branch into async at any time with no consequences and without complicating the flow. And an additional benefit is that rather than checking for errors after any operation you do, you choose where to check for errors. When a promise rejects, it skips everything else in the chain until it gets to a catch. So rather than running 4 async operations and doing an "if error do this" after each operation kind of deal, you can catch the error in once place and handle it once. Promises surpress the error in the promise chain until you choose to handle it, which is dangerous if you don't understand how promises work, but really useful once you do. There are really solid promise-based libraries for all common operations in node right now. When.js for general promises, composition, and coercion, rest.js for network requests, bookshelf and knex for database connection and orm stuff, etc. If you are a js developer, give them a shot! Don't get me wrong, I'm not trying to claim that this is better than any other language-level construct by any means, but if you are working in javascript, where you have to deal with javascript's limitations as a language, from experience I can say that working in an all-promises environment makes things quite pleasant.
Zig 语言通过设计让 async/sync 函数无需区分颜色
Zig 的函数是“色盲”的:https://kristoff.it/blog/zig-colorblind-async-await/
jedisct1查看英文原文
Zig’s functions are colorblind: https://kristoff.it/blog/zig-colorblind-async-await/
历次讨论
同类还值得读
开源应用架构
The Architecture of Open Source Applications
《开源软件架构》(AOSA)是一本在线免费书,邀请知名开源项目的作者亲自剖析其内部设计,涵盖 SQLAlchemy、LLVM、Audacity、Apache、Bash 等系统。
编程与软件工程上首页 9 次 · 2011–2024aosabook.org编辑精选编程语言简史:简短、不完整且多半错误
A Brief, Incomplete, and Mostly Wrong History of Programming Languages
一篇以戏谑、故意不准确的方式回顾编程语言史的博客文章,用夸张轶事和讽刺定义串起 Lisp、C、C++、Java、JavaScript、PHP 等语言。
编程与软件工程上首页 8 次 · 2009–2021james-iry.blogspot.comSQLite 是如何测试的
How SQLite is tested
SQLite 官方文档页,系统介绍 SQLite 的测试体系:多种测试方法(TCL 测试、模糊测试、回归测试、静态分析等)和海量测试用例,展示了这个小型开源项目如何靠极致测试获得…
编程与软件工程上首页 8 次 · 2009–2025sqlite.org编辑精选高级编程语言(2009)
Advanced programming languages (2009)
Matt Might 的经典文章,按用途推荐“高级编程语言”:如果追求就业就选主流语言,如果追求表达力、学术研究或创业,则推荐 Haskell、OCaml、Scala 等函数式语…
编程与软件工程上首页 8 次 · 2009–2022matt.might.net我的 Forth 与栈机器经历
Yosefk - My history with Forth & stack machines
作者以亲身经历回顾了使用 Forth 语言和栈式机器的历程:既赞叹其简洁优雅、代码量小、可自举的优点,也坦率指出栈操作在实际编程中的痛苦,以及 Forth 社区文化中的问题。
编程与软件工程上首页 8 次 · 2010–2024yosefk.com编辑精选