从盒子里解放出来
从盒子里解放出来
原文:https://medium.com/hackernoon/freedom-from-the-box-c229df788439
11 月 8 日,我在 Manhattan.js 网站上发布了这个演讲的版本。这些话是我在 15 分钟演讲中的演讲笔记的平滑版本。我很想继续重复这个演讲,所以如果你想亲自看,请告诉我!或许在 2018 年,我可以发表一些对每一点更深入的看法。
我总是以自我介绍开始我的谈话;虽然这看起来有点好笑,但就当我在说话吧。




你好。我是萨拉·格罗夫·亨尼格-巴勒莫。名字太多了!但如果对皇室来说足够好,对我来说也足够好。你可以叫我莎拉·GHP。我存在于互联网上,你可以随意对我说好话。

白天,我在 Kickstarter 的前端团队工作,

当我不在的时候,我制作数字艺术和数字艺术工具。这是我为电物体做的一个系列(👻长裂口💀),最近我和 Kate Sicchio 一起作为 algorave 团队的一部分,一直在开发一个用于现场编码视觉效果的工具——我们没有名字,我正在推动沙拉。

总的来说,我也是 LISPs 和编程语言理论的忠实粉丝,今天我非常兴奋地在这里和大家谈论一个我非常关心的话题——抵制在 Javascript 中添加静态类型系统。或者我喜欢这样想:

所以我要把类型、错误和误算作为一个概念来讲——这听起来很高尚——但实际上,就像这个演讲的占位符标题可能已经泄露了,这是因为

我讨厌类型。
我知道。我真的很讨厌他们。

“但是类型很酷,莎拉”你说。“你怎么会恨他们?”
我很高兴你问了。我很兴奋地告诉你。但是!在此之前,我想给出一个非常重要的警告。
这不是关于抨击事物的谈话。当我谈论生活中的事情时,我喜欢夸张和戏剧性,但我不会告诉你这种类型是坏的或愚蠢的,或者你喜欢他们是坏的或愚蠢的。
许多非常聪明、非常优秀的人——他们中的许多人我非常关心和尊重——喜欢类型系统。
相反,我想做这个演讲并开始这个对话是因为

我喜欢 Javascript,这整个静态输入的事情真的让我很不舒服。我在这里很利己。我不想失去我爱的东西。
我确实看到类型作为一个数学问题,或者像 Rich Hickey 最近建议的那样,作为一个难题是多么有趣。或者,你可能比我更看重编程的其他部分,所以你会为最终能实现你的前端梦想而激动不已。
但这就是为什么我不分享它们,以及一些关于我们能做些什么的想法。(我有解决办法!或者至少是建议!)但是首先的问题。


理由 1:我想成为自由人。
所以,这是充满活力的,灵活的极客的总的刻板印象,但这是真的。我觉得被类型系统束缚住了。
所有静态的、依赖类型的系统将会排除或不能表达一些完美的、无错误的程序。或者像我一些聪明的朋友说的:

类型系统可能会使你很难或者不可能做你不想做的事情,但是它们肯定不会使你想做的事情变得容易。这就是将安抚编译器的挑战置于程序员和运行代码之间的本质。
特别是,添加类似 Typescript 的东西干扰了我们在工作中提出的一些动态解决方案——代码是惯用的 Javascript,但需要动态类型生成。虽然这是可以通过努力解决的,但努力本身的必要性将我们带入下一个原因:

原因 2:努力与回报的比率是倾斜的。
甚至在减少的解决方案空间之外,使用类型系统需要很多额外的开销——认知的和其他的。不仅要花额外的时间来理解和实现系统兼容的类型,而且因为推理仍然不是很好,要使一个系统有用,你必须告诉编译器相当多的东西,正如我们的朋友将要学到的。

thanks will for letter me drag you into this
当然,如果在这里投入的时间占用了在其他地方花费的时间,这可能没问题,但是您仍然需要良好的测试来检查逻辑和集成问题以及其他问题,尽管人们喜欢这样想,

↗️,这实际上不是文档。所以我们仍然需要文档和测试,即使是类型。
好吧,那么说到原因 3:

原因 3:所有与编译器的斗争。
这有点颠倒了一些人对类型系统的喜爱:编译器在你运行程序之前就告诉你有问题。

然而,由于编译器和代码解析的性质,编译器通常会在距离我们必须修改以修复它的代码一定距离的地方定位错误。相比之下,解释器或实时编译器通常更接近问题。
当然,Elm 是礼貌的,Javascript 告诉我undefined不是一个函数,但是我会在一个精确的上下文中接受一个模糊的消息,而不是一个精确的、被替换的错误。
现在,第四个原因。

原因 4:它们不适合我,也不适合这个世界。
这个大而多的原因在很多方面是第一个和第三个原因的延伸。

因为我习惯于动态运行时,所以我倾向于通过编写一小段代码,然后运行它以确保它能够工作——通常是在 REPL 中(在 Javascript 中它只是你的浏览器控制台)。这与自顶向下的设计和实现的类型驱动方法不一致,即键入大块代码直到它们被编译。
我认为这不仅仅是一种偏好,

用微小的迭代开发其实更好。这是一种结构和立场,更符合编程的全部目的——完成某件事。

所以我已经提到过 Rich Hickey 几次——当我谈论编程时,我总是喜欢这样,因为如果我有编程英雄,他在那个列表上相当高——但在这种情况下,也是因为他刚刚在最近的 Clojure Conj 上做了一个演讲,花了很多时间解释为什么 Clojure 是动态的,为什么他做出了他所做的选择。
他提出了这一点,特别是在类型化研究语言的背景下,这一观点认为,居住、反映和改变世界是他感兴趣的编程类型的目的,可以说是所有非数学研究的编程。在这方面,

副作用和不完美不是要避免的敌人:它们是整体。
反映和改变不完美的、振荡的世界是程序的目的。
我认为,以这种小的、迭代的、认证的方式工作是你如何编写灵活的、容易理解的、并且随着你的需求变化而容易改变的程序。
这种方法产生的问题不太可能是类型错误,而更可能是逻辑错误——类型系统对此无能为力。

原因 5:类型系统强调我们最糟糕的冲动。
这个原因被我的朋友贾斯汀称为式的仇恨,作为一种修辞手段。他也不是完全错了。
因为对系统完美的追求不仅对我们想要在这个世界上实现的目标来说是不现实的,而且对人们来说也是一个不好的目标。

image from this cool blog
虽然我认为只有僵尸弗洛伊德才能真正解释程序员对正确性的迷恋,但很容易认为将完美的想法放在计算的核心是我们一些不太人道的影响的基础。我们正在具体化和定义的想法——有一个答案的想法——证明了剪切掉复杂性和嵌入我们的偏见是正确的。

如果你想知道更多,你应该把我灌醉——或者读读我的硕士论文,里面有同样的信息,但没那么大声嚷嚷。
最后一个原因是一个简短的原因:

原因 6:我就是没被说服。
我看到的演讲和例子从来没有让我兴奋过。但是我愿意相信我仍然可以改变主意——也许吧。
在那之前,让我们谈谈其他的选择。
因为在这一点上,你可能在想,“好吧,很好,莎拉,你认为处理类型是令人讨厌的,你不关心数学的可证明性,因为你是一些文科——野蛮,但是—”

”—类型错误是真实存在的。难道我们不应该为我们的用户创建更可靠的网站,即使这是以牺牲你的个人开发体验为代价的吗?毕竟,这就是钱的用途。”
在试图找出计算机科学中存在的处理错误的其他选择时,我遇到了误算的概念。

误算包含了我们可以编写出不按我们希望的方式运行的程序的所有方式。
研究员托马斯·皮特里切克将它们分类如下:

最窄的错误类别是失误,“你的想法是正确的,你试图以正确的方式编码,但你犯了一个语法错误,一个打字错误,或者说,一个复制粘贴错误。”
我总是认为这些错误不会出现太多,因为它们会立即崩溃,或者通过 linters 或基本的手工测试浮出水面。
另一方面,我们有

错误,“您在思考规范时犯了什么错误。”
这是一个额外的模糊范畴,因为规范的含义是它自己的一堆蠕虫,但是我认为错误是构建了错误的东西。
错误还可能包括不完整的规范、不断变化的环境和维护问题,在某些情况下,这些问题可以被视为不完善的编程的结果。
这些远远超出了我今天所能谈论的范围,但是它们可以通过许多不同类型的范例、过程和架构来解决。
最后,在中间,我们有

失败:“你有正确的想法,但编码(某部分)很差。”
这些是像逻辑错误和一些类型错误。例如,传递错误类型的函数,然后尝试执行内置函数。它们也是我将要关注的,因为它们包含了好的依赖类型试图修复的问题和它似乎遗漏的错误。

那么,还有哪些解决失败的方法呢?
第一种替代方法与证明程序正确几乎相反:

方法 1:让它崩溃。
这通常与 Erlang 联系在一起——它的确有最精彩的介绍视频——但是你不一定需要一个管理模型或者一个并发系统来应用它。
考虑 HTML。

当你犯了一个错误,它仍然呈现一个“坏”的页面,这通常是完全可用的,如果不漂亮的话。

一个更优雅的 Javascript-y 版本可能是使用try/catch或.catch()不仅抛出或记录一个错误,而且重试、恢复或继续,而不包含失败的部分。

React 16 使这种带有错误边界的方法成为可能。

这很好地进入了下一个补充选项:将您在类型定义文件上使用的时间用于关注好的缺省值。

所以,我不知道你是怎么想的,但是我遇到的主要失败(类型化似乎解决了这个问题)包括传递一个未定义的参数或者错误的标量类型,并试图对它进行操作或者索引一个不存在的属性。
例如,试图对一个参数调用.toUpperCase,结果这个参数丢失了或者是错误的数据类型。这是大多数支持在顶层 Javascript 上进行静态类型化的观点所关注的焦点。

第一个保护——在undefined上的保护——可以通过使用默认函数参数来添加。有趣的是,当null而不是undefined通过时,这些可能会失败,所以你必须同意你将通过离开undefined来“让”一个论点失败。

类似地,您可以为参数可能更复杂的情况创建一个默认对象。例如,在处理信用卡号码格式化功能时,我们有一个DEFAULT卡,它负责一些原本需要的保护和检查。

对于第二种情况,尝试对错误的标量进行操作,我喜欢创建传递函数,在传递函数中检查参数的标量类型,如果它不是函数所期望的类型,则要么完好无损地返回,要么返回默认值。
(注:我后来知道了这种情况的另一个术语是“创建一个总函数”)

如果你不喜欢为正在使用的每个小函数或管道编写类型检查,那么Maybe数据结构是一个不错的选择。(好吧,是的,这是一种类型,但嘿,即使有问题的范例也会产生好的想法!)单独使用时,它没有大多数缺点,并提供了一个很好的包装器,允许您在操作失败时设置默认值——假设您没有一个一切都变成Maybe的系统。
另一个好方法是找出你可能需要检查的地方,或者你的假设中可能有错误

正在使用创成式属性测试,例如使用testCheck.js。

这里的想法是使用自动生成的数据来测试代码的属性,其中属性是人们总是期望为真的描述:例如,排序一个排序的数组应该返回一个具有相同顺序的值的数组。
这种类型的测试的一个优点是,在选择要生成的数据类型和定义属性时,可以发现嵌入到函数中的假设。
在某些方面,我们甚至可以认为生成测试与使用类型具有相同的价值:

我们明确了我们对数据及其转换的期望——没有与编译器争论的缺点,也没有在无关紧要的地方过度指定的缺点。
另一种处理不完美世界的方法是我在谷歌原始的 map reduce 论文中遇到的:跳过记录。

在这种情况下,当处理器在一个记录上多次失败时,它最终会从处理中删除该记录,但在其他情况下会继续处理。对于页面排名的链接处理来说,这是一个非常好的解决方案:有足够多的记录仍然可以给出一个好的信号,那么为什么要在一个不完美的地方失败呢?
这在 Javascript 中会是什么样子?

一种方法是创建一个shortMap函数,它采用一个可能在数组的某些元素上失败的转换函数。如果失败,则从数组中移除该元素。或者,在转换失败的情况下,looseMap可以返回undefined,设置集合用于大量使用默认参数的系统。
考虑到语言的当前状态,这类似于我们之前讨论过的好的缺省值,特别是应用于批处理操作:映射、过滤等。展望未来,我喜欢想象一种内置松散批处理函数的动态语言会是什么样子,以及我们可能希望用它来解决的各种问题,特别是转换和显示内容流或人或机器生成的其他作品,或者我们知道我们正在处理不完整或未知记录的情况——更现实的数据处理。
在语言层面上,另一个解决方案可能是:

抛出更少的错误。起初这听起来有点傻,但是我们可以这样做

拥抱null,Clojure 和 Clojurescript 使用nil的方式,(通常称为无双关 ) 。
毕竟,当你试图索引一个嵌套的对象,并请求一个并不存在的深层键时,完全停止一个程序有什么用呢?为什么不直接返回null —值就是不存在。
好了,现在这变得非常假设性,最后一个,来自“误算”论文,是最假设性的:

我们可以学会忍受错误。在这种情况下,这意味着拥有工具来实时修复代码,并在错误发生时将其暴露出来。
这篇论文使用 Smalltalk 作为一种语言的例子,这种语言在有错误的情况下工作,并且找到了一些在现场编码和数字艺术实践中包容错误的论据。
起初,我试图认为这在实际软件中并不重要,因为虽然错误可以帮助发现好的美学,但它们似乎在接口实现中没有一席之地。

但是,如果界面是机器和世界之间的隔膜,那么开发工具让我们看到不适应的代码并快速解决它可能是创建工作的代码的最佳方式。
这就是把类型作为错误问题的解决方案的地方,我认为,这是一个重要的修辞。
如果我们不是建造一座工具塔来保护我们不受不完美的影响,而是将我们的努力花在与不完美和谐共处的工具上,从而与世界的真相和谐共处,会怎么样?

所以这是一个非常大且普遍的最终选择。但是要实现这一点,我们仍然需要制作可靠的软件——没有类型,并且以一种仍然令人愉快的方式编写。为此,以下是我今天在 Javascript 中尝试做的事情:

- 我制作小的、可组合的函数(这完全是另外一个补充话题,真的)。
- 我利用我们的老朋友信息隐藏和低表面积的 API。
- 我使用默认的参数,并尝试返回一些东西,而不是在错误的类型上出错。

我也试图记住什么类型是有用的。比如
- 生命危急的情况。如果一个程序让我们保持在离地球 30,000 英尺的高度,那么以可证明的完美性为目标可能是好的。
- 数据存储。例如,GraphQL 使用类型告诉后端和前端关于数据能力的有用信息的方式非常好。
- 高性能问题。有时,检查类型和提供选项的低效率可能与性能需求相反。(但比你想象的要少得多。)
不管怎样,这里面隐藏了大约 10 个演讲,但是今天我的时间差不多了。所以,我希望你感到鼓舞,去别处寻找解决你的一些问题,或者只是质疑一下海浪。
我从中提取的论文很容易阅读,完全值得一读——他的结论性建议,我不会破坏,确实想象了一个不同类型和自由可以彼此幸福相处的世界。
另外,如果你想听一些更高层次的理由,投资类型系统可能不是计算机科学的典范,我推荐 Rich Hickey 的 Conj 主题演讲和 LispCast 博客。
所有这些都在这个演讲的致谢名单和链接页面上,这是我放在网上的,我会在推特上发布链接。同时,非常感谢!


里奇·希基 2017 Conj 主题演讲 | Clojure and Types,摘自 LispCast|Nil punking 摘自 LispCast | 僵尸弗洛伊德 | 我的愚蠢论文 | 托马斯·皮特里切克《软件中的误算:学会带着错误生活》 | 反应 16 个错误边界 | TestCheck.js 【T15
就是这样!请记住,这是一份记录某个特定谈话的文件,而不是我对这个主题的所有想法。但是如果你真的想生气也没关系。❤️