功能与过程:保持它们分开。
#javascript #功能 #体系结构 #电脑科学

您是否知道保持功能和过程分开允许功能纯度(无副作用),并且在某些语言中还允许编译器优化?

“我们弄清楚了如何在单个编程隐喻中组合有效的计算和无效的计算,而不会使它们相互污染。类型系统使它们与众不同。” - Simon Peyton Jones ,Haskell的创建者,从2011年12月开始在this lucid interview中。

但是我们可以设法实现相同的目标(避免副作用相同的污染),但完全没有通过类型系统进行处理?我们甚至可以在JavaScript/Typescript中设法做到吗?即使没有asbiaoqian1?

的haskell-ist巫师

定义

a 过程是执行特定任务或一系列任务的程序中的代码块。它通常用于将相关代码分组在一起,并使其更易于阅读和维护。程序可能会将参数视为输入,但它们不会将值返回给呼叫者。

a 函数是执行特定计算或计算并将值返回到呼叫者的程序中的代码块。它需要一个或多个参数作为输入,并使用它们来计算返回值。函数可以用作表达式的一部分,分配给变量,也可以作为参数传递给其他函数或过程。

某些语言可能使用不同的术语,例如方法,子例程或子程序,但是函数和过程之间的基本区别保持不变。

问题

许多语言将这两个概念合并,并以返回void 的函数实现过程。这可能会混淆/complect它们的区别,从而使程序员从函数内部进行调用程序,从而使这些函数陷入 不纯粹的函数(这意味着他们通过I/O或突变状态之类的副作用)。应避免这种情况,特别是如果您关心调试性和Functional Core, Imperative Shell架构(请参阅31:56的Gary Bernhardt's Boundaries talk)(没有mocking,这使您的系统更加容易。

历史课

Pascal保留了彼此不同的功能和过程。

编程语言何时开始允许将功能和过程混合在一起?特别是在功能内部的调用程序(这会导致现在令人恐惧的副作用,从而失去了functional purityreferential transparency)?

可以从许多编程语言中的函数中调用过程,并且很长一段时间以来一直可以使用。实际上,这种能力自成立以来一直是许多编程语言的基本组成部分。

例如,在C编程语言中,可以从另一个函数内调用功能,并且一个函数也可以在需要时调用void函数(即过程)。自1970年代开发的早期版本以来,此功能就存在于C中。

同样,在1960年代末和1970年代初开发的Pascal编程语言中,程序和功能是不同的,但可以混合在一起。一个函数可以调用一个过程,只要在调用它的过程之前声明该函数。

许多其他编程语言,包括Python,Java和C ++等现代语言,也允许从彼此中调用过程和功能。因此,要回答您的问题,编程语言开始允许从功能中调用过程的时间点,因为此功能自编程语言的早期以来就已经存在。

为什么Pascal将程序和作用视为不同的?

通过将程序和功能视为独特的功能,Pascal允许程序员清楚地表明每个子程序的目的,并以反映其预期目的的方式使用它们。这可以使程序易于阅读,理解和维护。

此外,帕斯卡(Pascal)是在计算机资源相对有限的时代开发的,并且需要一种有效且精确的编程语言。将程序和功能视为独特的帮助确保编译器可以优化代码以提高效率,因为编译器确切地知道每个子程序的期望。

总的来说,将程序和功能视为帕斯卡(Pascal)的决定是一种设计选择,是为了促进编程中的清晰度,效率和精确性。

它如何允许Pascal编译器优化代码以提高效率?

Pascal编译器可以通过利用程序和功能具有不同属性和使用模式的事实来优化最大程度的代码。以下是如何完成此优化的一些示例:

  1. 堆栈管理:调用函数时,将参数和返回地址推到堆栈上。当功能返回时,堆栈将弹出以还原原始状态。相反,当调用一个过程时,只有参数被推到堆栈上,因为没有返回值可以保存。通过将功能和过程视为独特的,编译器可以生成更有效的代码来管理堆栈。

  2. 返回值处理:当功能返回值时,编译器会生成代码以将返回值存储在特定位置(例如寄存器或内存位置)中。相反,当调用一个过程时,没有存储返回值。通过将函数和过程视为不同,编译器可以优化返回值的代码并避免不必要的说明。

  3. 内存分配:调用函数或过程时,编译器必须为局部变量和临时数据结构分配内存。通过将函数和过程视为不同,编译器可以根据预期的使用模式优化每种子程序的内存分配策略。

  4. 内线:在某些情况下,编译器可以用函数的实际代码替换函数调用,即一种称为内部的技术。这可以消除函数调用的开销,并导致代码更快。但是,所有功能都可能不可能或不可能,因为它可以增加代码大小并降低代码可读性。

总的来说,通过将过程和功能视为不同的,Pascal编译器可以生成更有效的代码,从而利用每个子程序的唯一属性。这种优化可以改善由此产生的可执行文件的性能和资源使用。

在JavaScript中,我们可以从中获得什么?

在JavaScript中,过程被视为返回void 函数。建议保持程序和常规功能(确实会返回结果)截然不同,并防止函数内的呼叫程序(因为程序执行I/O或突变之类自己)。

将函数和过程视为JavaScript中不同的一种整洁的方法是将过程表示为返回Promise<void> async函数。然后,将保留正常同步函数进行计算。这样,您可以从程序的层次结构(异步函数)中添加出build your program in JavaScript,它们可以在其中具有正常同步函数的层次结构。您将有效地获得一个Functional Core, Imperative Shell architecture,该Functional Core, Imperative Shell architecture在他的Boundaries talk(31:56)中雄辩地详细介绍了。

JavaScript甚至会阻止您从(正常,同步)函数内部调用(等待)过程(async函数),因为在正常函数中无法调用/等待过程(async函数)!

>

我以我在研究此过程时发现的这句话结尾,这很好地总结了:

“我通常将我的不纯代码限制为异步函数。因此,承诺代表异步计算和不纯计算,类似于Haskell中的IO Monad。它可以很好地工作,因为您可以在异步函数中使用纯计算但是您不能在常规函数中直接使用异步计算。” - Aadith M. Shah