当人们谈论代数推理时,解释都是平淡无奇的,因为我们忽略了首先找出我们要与之对比的推理类型。当我们使用操作模型编写计算机代码时,我们确实会考虑我们写的是什么-那么我们使用的是什么方式的推理呢?我们能给它起个名字吗?我们能解释为什么它看起来与Lambda Man一直在说的那种推理如此不相容吗?
我建议,目前的编程文化可以分为两种方式,由以下关于编程行为的相互竞争的概念来解释:
我们都有一些需要在这两种观点之间转换的需要,但我们中的许多人变得更加固守其中的一种或另一种。
为了开始拼凑每一种程序员对计算机的定位,我想看看一个关于函数调用的有趣的代理问题。
当我编写表达式";fx&34;时,我可以这样描述我的编程行为:";我们将函数f应用于参数x&34;。
当机器执行程序时,我期望它将以参数x计算函数f。
但是,我作为作者的行为和机器作为自动机的行为之间的这种区别,在代数程序员的说法中是如此明显。如果我是Python程序员:
当机器执行我的程序时,我期望Python解释器将调用函数f。
那么谁调用该函数:我还是Python?答案是两者兼而有之;我是Python,它的动作就是我的动作,不管我是否在场并在REPL中键入内容,也不管我是否事先在一个可能在我不在的情况下运行的程序中编写了脚本。
在任何一种编程中,我们有时都会设身处地地为机器着想,以推断我们所写内容的预期结果。但是这种想象力如何运作在很大程度上取决于我们是在外面还是在里面。当我们追踪一个可操作的程序流时,程序的文本形成了一个我们可以在其中移动的空间,每个变量都是一个活生生的雕像,开始说话。代数表达式计算是一件更加枯燥乏味的事情,我们仍然坐在办公椅上。求值就是用另一种形式重写表达式;它不会将我们带入与最初编写代码时不同的标题空间。
当一位经验丰富的计算机内部作者开始使用一种迫使我们从外部处理程序的编程语言时,我们可以期待这样的问题:如果我有一个IO字符串,那么我怎样才能找到这个字符串?
虽然其他人已经详细讨论了这个问题,但我想在这里引起注意的是,误解源于试图将内部概念映射应用于强烈外部的编程模型。IO字符串是生成字符串结果的过程。因此,如果我站在Haskell程序中,手里拿着这样一个东西,那么我自然可以运行该进程并获得字符串。但我们没有这样的价值观,因为我们不是进去拿东西的。我们仍停留在文本编辑器中,编写定义。一种这样的定义可能是针对由机器组成的进程:1.首先运行某个IO字符串进程;然后2.对结果字符串执行某些其他操作。
对于JavaScript程序员来说,这并不是一项陌生的任务,他们知道不能从承诺中获得价值-我们所能做的就是制定计划,说明一旦实现了承诺要做什么。JavaScript程序员虽然在计算机内部,但在事件循环之外。当我的回拨被唤醒时,我做我的工作,然后我回到睡眠中等待另一场演出。
全局变量这个术语揭示了我们心理图景中一些有趣的东西。这样的变量不会覆盖全球,甚至不会覆盖本地网络。一个像全球这样宏伟的词指的是什么范围?谦虚地说,一个过程的范围。如果我是内核开发人员,也可以是整台机器。当我编写代码进行操作时,我居住在一个很小的世界中--我的蓝色小大理石上的陆地是我可以访问的内存段。
当我从运筹学转换到代数编程时,首先我了解到没有全局变量,然后这个术语开始完全从意识中淡出。作为一名Haskell程序员,我不是在桌子上的一个小地球仪里;我生活在地球上,我有类型定义。在这些定义中,可能有一个表示进程状态的数据类型,TRUE。但是这个数据类型不是我的领域,我为程序服务而编写的绝大多数定义都不是它的函数。
Java类中的私有成员变量只能从该类内部访问。或许可以将介词理解为字面上指的是类定义的词法范围-即,在左花括号和右大括号之间编写的代码。但是,我们在这里是否使用了更深层的容器隐喻呢?也许这里只有我一个人,但我把这个实例看作一个房间,当我在那个房间里工作时,成员就是我可以支配的东西。
在Haskell中,我们还有词法作用域,以及具有导出或未导出定义的模块的概念,这在许多方面反映了公共/私有字段的区别。但我没有在心目中进入模块的经验,这与在Java实例中进行推理的意义相同。我相信正是Java对具有可变状态的模块的耦合鼓励了这种空间推理。
当您使用单步调试器时,您实际上是在单步执行程序!无论您使用的是Python还是Haskell的调试工具,都是如此。虽然代码可能是代数的,但当我们使用步骤调试器时,我们总是从操作的角度来看待它。
最近,我喜欢将我的角色称为";作者,而不是程序员--不管文件扩展名是";.md";还是";.hs";。这是因为我再也不觉得自己在电脑里工作了。我坐在电脑前工作,我写关于程序的东西,虽然我写的很多东西都可以由机器执行,但我不会经常迷失在里面,因为我仍然安全地呆在外面。