`
daojin
  • 浏览: 678046 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

代码的颜色是什么?

 
阅读更多
什么颜色是你的功能? ↩↪
2015年2月1日 代码 飞镖 去 JAVASCRIPT 语言 卢阿
我不知道你的情况,但是没有什么能让我在早上很喜欢老式的编程语言咆哮。 它激发了血液看到有人串出一种普通人使用的“blub”语言之一,在偷偷摸摸访问StackOverflow之前,他们在一天中混淆了它。

(同时,你和我,只使用最开明的语言,凿锋利的工具,专为像我们这样的专家级工匠修整而成。

当然,作为所述熨平板的作者 ,我冒了风险。 我嘲笑的语言可能是你喜欢的语言! 在没有意识到的情况下,我可以让这个乌合之众进入我的博客,用干草叉和火把准备好,而我的傻瓜小册子可能会引起他们的愤怒!

为了保护自己免受火焰的侵袭,并避免冒犯你可能微妙的敏感性,相反,我会大声说出我刚刚编好的一种语言。 一个稻草人,其唯一的目的是燃烧。

我知道,这似乎毫无意义的权利? 相信我,到最后,我们会看到他的脸部(或脸部!)已经被涂在他的吸管上了。

一种新的语言
为一篇博客文章学习一种全新的(糟糕的)语言是一项艰巨的任务,所以我们假设它大部分与你我已经知道的类似。 我们会说它有像JS一样的语法。 大括号和分号。 if ,等等,这是编程石窟的通用语言 。

我选择JS 并不是因为这是这篇文章的内容。 只是它是你的语言,你是普通读者的统计表示,最有可能是grok。 瞧:

  function thisIsAFunction () { return "It's awesome" ; }
因为我们的稻草人是一种现代 (低劣)的语言,我们也有一流的功能。 所以你可以像这样做:

  // Return a list containing all of the elements in collection // that match predicate. function filter ( collection , predicate ) { var result = []; for ( var i = 0 ; i < collection . length ; i ++ ) { if ( predicate ( collection [ i ])) result . push ( collection [ i ]); } return result ; }
这是高阶函数之一,正如名字所暗示的那样,它们是优雅的,因为所有的东西都出来了,而且非常有用。 你可能习惯了他们用来收集藏品,但是一旦你内化了这个概念,你就开始把它们用在任何地方。

也许在你的测试框架中:

  describe ( "An apple" , function () { it ( "ain't no orange" , function () { expect ( "Apple" ). not . toBe ( "Orange" ); }); });
或者当你需要解析一些数据时:

  tokens . match ( Token . LEFT_BRACKET , function ( token ) { // Parse a list literal... tokens . consume ( Token . RIGHT_BRACKET ); });
所以你去镇上写了各种各样的令人敬畏的可重用的库和应用程序传递函数,调用函数,返回函数。 Functapalooza。

你的功能是什么颜色?
除了等待。 这是我们的语言变得棘手的地方。 它具有以下特点:

1.每个功能都有一个颜色。

每个函数匿名回调或常规名为one-是红色或蓝色。 由于我的博客代码荧光笔无法处理实际颜色,因此我们会说它的语法如下所示:

  blue • function doSomethingAzure () { // This is a blue function... } red • function doSomethingCarnelian () { // This is a red function... }
语言中没有无色的功能。 想做一个功能? 必须选择一种颜色。 他们的规则。 而且,实际上,你还必须遵守更多规则:

2.你调用函数的方式取决于它的颜色。

想象一下“蓝色调用”语法和“红色调用”语法。 就像是:

  doSomethingAzure (...) • blue ; doSomethingCarnelian () • red ;
在调用函数时,您需要使用与其颜色相对应的调用。 如果你弄错了 - 在括号之后用•blue调用红色函数,反之亦然 - 它会做一些不好的事情。 疏远你童年时代一个久违的噩梦,就像一只小蛇一起躲在床底下的蛇。 那跳出你的显示器,吸出你的玻璃状幽默。

讨厌的规则,对吧? 哦,还有一个:

3.您只能从另一个红色功能中调用红色功能。

您可以使用红色功能调用蓝色功能。 这是犹太教:

  red • function doSomethingCarnelian () { doSomethingAzure () • blue ; }
但是你不能走另一条路。 如果你尝试这样做:

  blue • function doSomethingAzure () { doSomethingCarnelian () • red ; }
那么,你会得到老Spidermouth夜间小丑的访问。

这使得像我们的filter()例子那样编写高阶函数更复杂。 我们必须为它选择一种颜色,并影响我们允许传递给它的函数的颜色。 显而易见的解决方案是使filter()变为红色。 这样,它可以使用红色或蓝色功能并调用它们。 但后来我们遇到了这种语言的毛茸茸的下一个痒点:

4.红色功能比较痛苦。

目前,我不会精确地定义“痛苦”,但是想象一下,每次他们调用红色函数时,程序员都要跳过某种恼人的箍环。 也许它真的很冗长,或者你不能在某些类型的陈述中做到这一点。 也许你只能用线数来打电话给他们。

重要的是,如果你决定做一个红色的功能,那么每个使用你的API的人都会想要吐出你的咖啡和/或存储一些更少的咸味液体。

那么明显的解决方案就是永远不要使用红色函数。 只要把所有的东西都变成蓝色,你就回到了理智的世界,所有的功能都具有相同的颜色,这相当于它们都没有颜色,这相当于我们的语言不是完全愚蠢的。

唉,虐待狂的语言设计师 - 我们都知道所有的编程语言设计师都是虐待狂,我们不是吗? - 在我们身上刺出了最后一根刺:

5.一些核心库函数是红色的。

平台中内置了一些功能,我们需要使用的功能,我们无法自己写,只有红色。 在这一点上,一个合理的人可能会认为这种语言恨我们。

这是函数式编程的错!
你可能会认为这里的问题是我们试图使用更高阶的函数。 如果我们不停地在所有这些功能性的烦恼中喋喋不休,并写出像上帝一样的正常的蓝领一阶功能,我们会让所有的心痛得意忘形。

如果我们只调用蓝色功能,请将我们的功能设为蓝色。 否则,将它变成红色。 只要我们从不制作接受函数的函数,我们就不必担心试图使用“多色函数颜色”(多色?)或任何这样的废话。

但是,唉,高阶函数只是一个例子。 这个问题在我们想要将程序分解成可重用的单独函数的任何时候都是普遍存在的。

例如,假设我们有一个很好的代码块,我不知道它是通过一个图表来实现Dijkstra算法,这个图表表示您的社交网络彼此之间有多大的差异。 (我花了太长时间来试图确定这样的结果甚至会代表什么。传递不需要性?)

后来,你最终需要在其他地方使用这个相同的代码块。 你做了自然的事情,并将其提升为一个单独的功能。 你可以从旧地方和你使用它的新代码中调用它。 但是它应该是什么颜色? 显然,如果可以的话,你会把它变成蓝色,但如果它使用了那些令人讨厌的红色核心库函数呢?

如果你想称之为蓝色的新地方呢? 你必须把它变成红色。 然后,你将不得不把它称为红色的功能。 啊。 无论如何,你必须不断思考颜色。 这将是你的泳装在沙滩上度假的开发。

一个多彩的寓言
当然,我在这里没有真正谈论色彩,是吗? 这是一个寓言,一个文学伎俩。 Sneetches不是关于肚子上的明星,而是关于种族。 到现在为止,您可能会对真实代表什么颜色有一个疑问。 如果不是,这里有一个大的揭示:

红色功能是异步的。

如果您在Node.js中使用JavaScript进行编程,那么每次您定义一个函数以通过调用回调“返回”一个值时,您只需制作一个红色函数。 回顾一下这些规则列表,看看我的隐喻是如何叠加起来的:

同步函数返回值,异步函数不返回,而是调用回调函数。

同步函数将其结果作为返回值,异步函数通过调用您传递给它的回调函数给出结果。

您不能从同步函数调用异步函数,因为直到异步函数稍后完成才能确定结果。

由于回调,异步函数不在表达式中组成,具有不同的错误处理,并且不能与try/catch或许多其他控制流语句一起使用。

Node的整体表现是核心库都是异步的。 (尽管他们确实拨回了这个号码,并开始添加很多东西的___Sync()版本。)

当人们谈论“回拨地狱”时,他们正在讨论在他们的语言中使用红色功能有多恼人。 当他们创建4089个用于执行异步编程的库时 ,他们试图在图书馆级别应对语言强加给他们的问题。

我保证未来会更好
Node社区的人们已经意识到回调是很长时间的痛苦,并且已经寻找解决方案。 让一群人兴奋的一种技巧是承诺 ,你可能也会通过他们的说唱歌手名称“期货”知道。

这些都是围绕回调和错误处理程序的顶层包装。 如果您想将回调和错误回传作为概念传递给函数,那么承诺基本上就是这个概念的一个具体化 。 这是表示异步操作的第一类对象。

我只是在该段中挤出了一堆花哨的PL语言,所以它可能听起来像一个甜蜜的交易,但它基本上是蛇油。 承诺确实使异步代码更容易编写。 他们组成更好一点,所以规则4并不那么繁琐。

但是,说实话,这就像在肠道内被打孔和在私人打孔之间的区别。 不痛,是的,但我认为任何人都不应该为价值主张感到激动。

您仍然无法将它们用于异常处理或其他控制流程语句。 您仍然无法调用从同步代码返回未来的函数。 (你可以 ,但是如果你这样做,后来维护你的代码的人会发明一个时间机器,及时回到你做这件事的时刻,并用#2铅笔刺你的脸。)

你仍然将整个世界划分为异步和同步的一半,以及所有的不幸。 所以,即使你的语言具有承诺或期货的特征,它的脸看起来像我的稻草人那样。

(是的,这意味着即使是Dart也是我工作的语言,这就是为什么我非常兴奋,一些团队正在尝试其他并发模型 。)

我正在等待解决方案
C#程序员现在可能会感觉非常自在(这种情况越来越受到人们的青睐,因为Hejlsberg和公司已经将甜蜜的功能融入了语言中)。 在C#中,您可以使用await关键字来调用异步函数。

这使您可以尽可能轻松地进行异步调用,只需添加一个可爱的小关键字即可。 您可以在表达式中嵌套await调用,在异常处理代码中使用它们,将它们填充到控制流中。 发疯。 让下雨await电话,就像他们为你的新的说唱专辑提前获得的美元一样。

异步等待很好,这就是我们将它添加到Dart的原因。 它使编写异步代码变得更容易。 你知道一个“但是”即将到来。 它是。 但是......你仍然把世界分成两部分。 那些异步函数更容易编写,但它们仍然是异步函数 。

你仍然有两种颜色。 异步等待解决了烦人的规则#4:它们使红色函数调用比蓝色函数差得多。 但所有其他规则仍然存在:

同步函数返回值,异步函数返回该值的Task<T> (或Dart中的Future<T> )包装。

同步功能只是叫,异步需要await 。

如果你调用一个异步函数,当你真的需要T时,你有这个包装器对象。 除非你让你的函数异步并等待它,否则你不能解开它。 (但请参阅下文。)

除了自由的await ,我们至少解决了这个问题。

C#的核心库实际上比异步更古老,所以我猜他们从来没有这个问题。

它更好。 我会在一周中的任何一天通过无回调或期货进行异步等待。 但是如果我们认为我们所有的麻烦都消失了,我们就在撒谎。 一旦开始尝试编写高级函数或重新使用代码,您马上就会意识到颜色仍然存在,遍布整个代码库。

什么语言没有着色?
所以JS,Dart,C#和Python都有这个问题。 CoffeeScript和大多数编译为JS的其他语言也这样做(这就是Dart继承它的原因)。 我认为,即使ClojureScript已经尝试用core.async的东西来推动它,但它仍然存在这个问题。

想知道一个没有? Java的。 我知道,对吧? 你多久会说“是的,Java才是真正做到这一点的人。”? 但是你去了。 在他们的防守中,他们正在积极尝试通过转向期货和异步IO来纠正这种疏漏。 这就像是一场比赛。

C#也可以避免这个问题。 他们选择了颜色。 在添加async-await和所有Task<T>内容之前,您只需使用常规的同步API调用。 还有三种没有这个问题的语言:Go,Lua和Ruby。

任何猜测他们有什么共同点?

线程。 或者更准确地说: 可以在两者之间切换的多个独立的调用堆栈 。 它们不是严格需要操作系统线程。 Go中的Goroutines,Lua中的coroutines以及Ruby中的纤维是完全足够的。

(这就是为什么C#有这么一点警告的原因,你可以通过使用线程来避免C#中异步的痛苦。)

回忆过去的行动
根本的问题是“如何在操作完成时停止离开你的地方”? 你已经建立了一些大的调用堆栈,然后你调用一些IO操作。 为了性能,该操作使用操作系统的底层异步API。 你不能等待它完成,因为它不会。 你必须返回到你的语言的事件循环,并给操作系统一些时间旋转,然后才能完成。

一旦完成,你需要恢复你正在做的事情。 一种语言“记住它在哪里”的通常方式是调用堆栈 。 它跟踪当前正在调用的所有函数以及指令指针在每个函数中的位置。

但要做异步IO,你必须展开放弃整个C调用堆栈。 一种Catch-22。 你可以做超级快速的IO,你不能对结果做任何事情! 每个在其内部具有异步IO的语言 - 或者在JS(浏览器的事件循环)的情况下 - 以某种方式处理这个问题。

以其不断向前的回调函数封装所有这些框架。 当你这样做时:

  function makeSundae ( callback ) { scoopIceCream ( function ( iceCream ) { warmUpCaramel ( function ( caramel ) { callback ( pourOnIceCream ( iceCream , caramel )); }); }); }
这些函数表达式中的每一个都关闭了它周围的所有环境。 这会将像iceCream和caramel这样的参数移动到堆栈上。 当外部函数返回并且调用堆栈被删除时,它很酷。 这些数据仍然在堆上浮动。

问题是你必须手动地将这些步骤中的每一个步骤都进行细化。 实际上这个转换的名字是: 延续传球风格 。 它是由70年代的语言黑客发明的,作为编译器内部使用的中间代表。 代表恰好使编译器优化更容易完成的代码是一种非常奇怪的方式。

没有人再想过程序员会写这样的实际代码 。 然后Node出现,突然间我们假装成编译器后端。 我们哪里出了问题?

请注意,承诺和期货实际上并不会为您购买任何东西。 如果你已经使用过它们,你就知道你还在手工创建巨大的函数文字。 你只是将它们传递给.then()而不是异步函数本身。

等待生成的解决方案
异步等待确实有帮助。 如果你剥离你的编译器的头骨,看看它在await调用时做了什么,你会发现它实际上在做CPS转换。 这就是为什么你需要在C#中使用await的原因 :这是编译器说“在这里把功能打破一半”的线索。 await之后的所有内容都被提升为一个代表您综合的新功能。

这就是异步等待在.NET框架中不需要任何运行时支持的原因。 编译器将它编译成一系列可以处理的链式关闭。 (有趣的是,闭包本身也不需要运行时支持, 它们被编译为匿名类,在C#中,闭包确实是一个穷人的对象 。)

你可能想知道我什么时候会启动发电机。 你的语言是否有yield关键字? 然后它可以做一些非常相似的事情。

(事实上​​,我相信发生器和异步等待是同构的,我的硬盘的一个黑暗角落里浮现了一些代码,它只使用异步等待来实现发生器风格的游戏循环。)

我在哪里? 啊对。 所以在回调,承诺,异步等待和生成器的情况下,最终你最终会采用异步函数并将其拖入堆栈中的一堆关闭中。

你的函数将最外层的函数传递到运行时。 当事件循环或IO操作完成时,它会调用该函数,并从中断处继续。 但那意味着你上面的一切都必须回归。 你仍然需要放松整个堆栈。

这是“红色功能只能由红色功能调用”的规则来自哪里。 您必须将整个callstack重新封装回main()或事件处理程序。

统一的调用堆栈
但是,如果您有线程(绿色或OS级别),则不需要这样做。 您可以暂停整个线程并直接跳回OS或事件循环, 而无需从所有这些函数中返回 。

Go是我认为最美妙的语言。 只要您执行任何IO操作,它就会暂停该goroutine并恢复其他未被IO阻止的其他操作。

如果您查看标准库中的IO操作,它们看起来是同步的。 换句话说,他们只是做了工作,然后在完成后返回结果。 但并不是说它们在JavaScript中意味着它们是同步的。 其他Go代码可以在这些操作之一正在等待时运行。 这就是Go 消除了同步和异步代码之间的区别 。

Go中的并发性是您选择如何为程序建模的一个方面,而不是标准库中每个函数的颜色。 这意味着我上面提到的五条规则的所有痛苦都完全消除了。

所以,下次你开始告诉我一些新的热门语言,以及它的并发性故事有多棒,因为它有异步API,现在你会知道为什么我开始磨牙了。 因为这意味着你回到了红色功能和蓝色功能。

    

嗨! 我是Bob Nystrom ,左边那个。

我写了一本名为Game Programming Patterns的书。 我正在编写另一本名为Crafting Interpreters的书

您可以在本网站的robert给我发电子邮件,或者在@munificentbob twitter上关注我。

别处
在github上的代码
推特推特
照片在500px
flickr上的照片
分类
代码 67
语言 41
喜鹊 24
C-sharp 13
飞镖 13
游戏开发 12
java 10
cpp 8
游戏模式 6
解析 6
roguelike 6
设计 5
去 5
js 4
书 3
c 3
雀 3
python 3
红宝石 3
博客 2
f-sharp 2
lua 2
音乐 2
ai 1
测试版 1
blogofile 1
游戏 1
jasic 1
javascript 1
oop 1
优化 1
oscon 1
政治 1
方案 1
打字稿 1
可视化 1
所有74篇文章 ...

这个博客是使用jekyll构建的。 源代码回购是这里 。

©2008-2014 Robert Nystrom




英语原文:
Just make everything blue and you're back to the sane world where all functions have the same color, which is equivalent to them all having no color, which is equivalent to our language not being entirely stupid.
更好的翻译建议
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics