在Racket程序中使用Scheme库

8
我在Racket中编写了一个程序(源代码在一个名为.rkt的文件中,顶部有#lang racket)。我还用(大多数)可移植的R7RS Scheme编写了一个库。我能以一种干净的方式在程序中使用这个库吗?
我的目标是使该库在Scheme实现之间广泛可移植(至少是符合R7RS标准的,最好也包括其他实现)。有第三方R7RS shim for Racket,但据我所知,它要求我在源文件顶部键入#lang r7rs。我认为这个#lang指令会让除Racket之外的Scheme感到困惑。
我能把我的库核心放在一个或多个可移植的.scm源文件中,然后有一个带有#lang r7rs指令的.rkt文件,告诉Racket如何包含这些可移植文件吗?Racket是否理解某种库定义文件,例如snow-fort上使用的.sld
我尝试在所有Racket文档中寻找,但无法找到任何讨论此事的内容。我也没有找到通用的Scheme可移植性FAQ或最佳实践文档。

1
R6RS使用#!r6rs,在Racket中被理解为. #lang r6rs。我认为#!r7rs应该可以工作,但我找不到相关文档。 - Sylwester
不幸的是, R7RS-small 规范没有提到 #!r7rs。奇怪的是它在旧标准中有出现但在新标准中却没有。 - Lassi
1
FYI:Racket Slack 频道的人(包括实现 #lang r7rs 的人)看到并讨论了你的问题,但似乎没有好的答案。如果你注册一个账户,就可以在这里看到讨论内容:https://racket.slack.com/archives/C06V96CKX/p1553004312909000 - Sorawee Porncharoenwase
1个回答

3
我成功地将Racket和R7RS代码混合使用于实际工作,并在GitHub上展示了这种技术的示例。
以下是该存储库中readme文件的副本:

Racket R7RS桥接器

Racket没有内置的R7RS支持。可以使用Alexis King的第三方软件包r7rshttps://github.com/lexi-lambda/racket-r7rs。尽管它不是Racket的官方组成部分,但对我来说很有用(我使用了一个相当复杂的库来进行HTML解析,并在其之上编写了一些非常复杂的字符串处理和树遍历操作,因此这绝对对实际工作有用)。

您可以通过raco pkg install r7rs安装桥接器。请注意,info.rkt列出了r7rs的依赖项,这对于Heroku等平台是必要的。

模块

  • app -- 一个Racket应用程序
  • lib -- 被app使用的一个R7RS库
  • sublib -- 被lib使用的一个R7RS库

模块的文件组成

因此,app只需要一个文件app.rkt,就像任何普通的Racket模块一样。

但是,libsublib各需要3个文件。 lib.scm是Scheme代码。 lib.sld是Scheme库定义。而lib.rkt是它的Racket包装器。技术上来说,您可以将lib.sldlib.scm合并为一个文件,但是将它们分开更加清晰。您也可以直接将所有Scheme代码复制到lib.rkt中,但是那么您无法将其导入其他Scheme中。

请注意,lib.scm在顶部没有(import ...)表单。导入是在lib.slddefine-library表单中。 define-library表单使用(include ...)将实际代码包含在lib.scm中。

lib.rkt的工作只是向Racket发出#lang r7rs指令,然后包括Scheme代码。它首先需要(import (scheme base)),以便我们可以使用includeexport。所包含的.sld文件从Scheme标准库中导入库所需的所有内容。
请注意,lib依赖于sublib,但sublib未被导入到lib.slddefine-library中。相反,lib.rkt必须加载lib及其所有依赖项:它包含(include "sublib.sld")以及显然的(include "lib.sld")
因此,lib.sld仅从Scheme标准库中导入内容,而lib.rkt则从所有自定义库中导入内容。我不得不采用这种方法,因为如果我将sublib放在(define-library ...)导入中,我找不到Racket模块查找器来找到它。我并没有尝试过很努力,所以可能有一种方法可以使其正常工作。

可变列表 vs 不可变列表

默认情况下,Racket使用不可变的cons单元(由Racket的cons创建,满足pair?),而R7RS使用可变的cons单元(由Racket的mcons创建,满足mpair?)。也就是说,当您在Scheme端调用cons时,它实际上会使Racket看起来好像您在Racket端调用了mcons。可变的cons意味着您可以使用Scheme的set-car!set-cdr!进行原地更改,而不可变cons的car和cdr在初始cons之后无法更改。
默认情况下,Racket使用{花括号}而不是(普通括号)来显示由可变cons组成的列表。当您通过R7RS-Racket边界传递列表时,这将造成麻烦。您可以通过更改print-mpair-curly-braces参数以使用普通括号打印,但对于许多事情,将您的列表(和树)从可变转换为不可变可能更容易。
我不知道Racket R7RS接口是否允许您在Scheme端创建不可变cons。如果有一个选项,让Scheme cons创建不可变的cons将是很好的(在这种情况下,set-car!set-cdr!会导致错误,这对仅使用不可变数据结构的代码来说是可以的)。

如何获取R7RS库

Alex Shinn在http://snow-fort.org/上收集了许多R7RS库。

总体而言

总的来说,您可以通过一些工作将R7RS和Racket混合使用,并且对于简单情况,您的代码库会保持相当清晰。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接