没有手动内存管理或运行时垃圾回收的基于类型的内存安全性?


13

假设我们想要一种有类型的,纯函数式的编程语言,例如Haskell或Idris,其目标是没有垃圾回收且没有运行时(或至少不超过C和Rust“运行时”)的系统编程。某种或多或少可以在裸机上运行的东西。

不需要手动内存管理或运行时垃圾回收的静态内存安全选项有哪些?使用类似于Haskell或Idris的纯函数类型系统如何解决该问题?


您是在说您希望将语言中的类型用作避免垃圾回收的一种方法吗?基本问题出现在功能评估中。将函数评估为闭包,该闭包封装了当前的运行时环境。那是必须进行垃圾收集的主要来源。除非您更改函数的输入规则,否则我看不到类型将如何帮助您。Java和其他具有破坏性抽象的语言通过改变闭包的形式来解决此问题:它们禁止需要垃圾收集的引用。λ
安德烈·鲍尔

当然,Rust必须通过其所有权模型和借用检查器来解决功能评估和关闭的相同问题?内存管理只是意味着知道这些值可以使用多长时间,还有哪些其他值依赖于它们,并在它们失效时销毁未使用的值,对吗?因此,我想我真的是在问是否可以将内存管理封装在一组类型中,以通过类型系统检查其正确性,而无需通过添加全新的所有权系统和“借用”来扩展语言或编译器的基本机制。检查器”(这是Rust的方法)。
Chase

马丁·霍夫曼(Martin Hofmann)的LFPL呢?它具有特殊的基本类型“钻石”,在其上强制执行线性类型规则,从而允许类型考虑基本内存使用情况(分配/取消分配)。那会朝着您所谈论的方向发展吗?
Damiano Mazza

Answers:


18

粗略地说,安全的手动内存管理有两种主要策略。

  1. 第一种方法是使用一些子结构逻辑,例如线性逻辑来控制资源使用。自线性逻辑诞生以来,这种想法基本上就已经浮出水面,并且基本上是基于这样的观察:通过禁止收缩的结构规则,每个变量最多使用一次,因此没有混叠。因此,就地更新和重新分配之间的差异对于程序是不可见的,因此您可以使用手动内存管理来实现您的语言。

    这就是Rust所做的(它使用仿射类型系统)。如果您对Rust风格语言的理论感兴趣,那么最好阅读的论文之一是Ahmed等人的L3:带有位置的线性语言。顺便提一句,提到的LFPL微积分Damiano Mazza也是线性的,在RAML语言中具有从中衍生的完整语言

    如果您对Idris样式的验证感兴趣,则应查看Xi等人的ATS语言,这是一种Rust / L3样式语言,支持基于Haskell样式索引类型的验证,仅使证明无关紧要且线性,可以提供更多信息控制性能。

    更为激进的依赖方法是Microsoft Research开发的F-star语言,它是完全依赖类型理论。该语言按照Nanevski等人的Hoare类型理论(甚至是我自己的线性和从属类型集成)的精神,具有一个带有前置条件和后置条件的单子接口,并且具有可编译为低级C代码的已定义子集。 -实际上,他们已经将经过验证的加密代码作为Firefox的一部分提供了!

    显然,F-star和HTT都不是线性类型的语言,但是它们的单子的索引语言通常是基于Reynold和O'Hearn的分离逻辑的分离逻辑是与线性逻辑相关的子结构逻辑,已经获得了巨大的成功。指针程序的Hoare逻辑的断言语言。

  2. 第二种方法是简单地指定要执行的程序集(或所需的任何低级IR),然后使用某种形式的线性或分离逻辑直接推断出其在证明助手中的行为。本质上,您可以将证明助手或依赖类型的语言用作仅生成正确程序的非常精美的宏汇编器。

    Jensen等人的针对低级代码高级分离逻辑就是其中的一个特别纯粹的例子-它为x86汇编构建了分离逻辑!但是,与此相关的项目很多,例如普林斯顿大学的“ 验证软件工具链”和耶鲁大学的CertiKOS项目。

所有这些方法都将像Rust一样“感觉”,因为通过限制变量的使用来跟踪所有权是所有方法的关键。


3

线性类型和分隔逻辑都很棒,但是可能需要程序员付出很多努力。例如,在Rust中编写安全的链接列表可能非常困难。

但是,还有另一种方法虽然需要较少的严格保证,但所需的程序员工作量却少得多。一个(相当古老的)工作流是通过使用(通常是一堆)区域来保证内存安全的。使用区域推断,编译器可以静态地确定分配的数据应该进入哪个区域,并在超出范围时取消分配该区域。

区域推理被证明是安全的(不能释放可分配的内存),并且需要最少的程序员干预,但是它不是“全部”的(即它仍然可以泄漏内存,尽管绝对比“不做任何事情”要好得多),因此通常将其与GC在实践中。的MLtonML Kit编译器使用区域来消除大多数GC调用,但是它仍然具有GC,因为否则它仍会泄漏内存。根据一些早期的区域先驱者所说,区域推理实际上并不是为此目的而发明的(我认为是为了自动并行化)。但是事实证明它也可以用于内存管理。

首先,我想说一下Mads Tofte和Jean-Pierre Talpin的论文“使用一堆区域实现类型化的按值调用λ微积分”。有关区域推理的更多论文,请查找M. Tofte和J.-P的其他论文。Talpin,Pierre Jouvelot的一些作品,以及Greg Morrisett,Mike Hicks和Dan Grossman关于旋风的一系列论文。


-2

“裸机”系统的一个简单方案是简单地禁止所有运行时内存分配。请记住,即使C malloc/free对也需要运行时库。但是,即使在编译时定义了所有对象,也可以以类型安全的方式定义它们。

这里的主要问题是在程序运行时创建的纯功能语言中不变值的虚构。实际的硬件(当然还有裸机系统)依赖可变的RAM,而RAM的供应有限。实际上,功能语言实现的运行时会在创建新的“不可变”值时动态分配RAM,并在不再需要“不可变”值时进行垃圾回收。

对于大多数有趣的问题,至少某些值的生存期取决于运行时(用户)的输入,因此无法静态确定生存期。但是,即使生命周期不取决于输入,它也可能是非常重要的。通过简单的程序,只需按顺序检查每个数字,然后对所有素数进行校验,即可反复查找素数sqrt(N)。显然,这种需求可以保持素数,并可以回收用于非素数的内存。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.