在Racket中要求vs load vs include vs import

时间:2018-01-31 18:04:07

标签: import module include racket require

Racket文档表明,Racket具有单独的表单:requireloadincludeimport。许多其他语言只包含其中一种,通常用于同义词(尽管显然存在语言特定差异,例如C中的#include和Java中的import

由于Racket拥有所有这四种,因此每种产品之间有什么区别?我应该使用哪种产品?此外,如果每个都有特定用途,我何时应该使用替代类型?另外,this question似乎表明require(与provide配对)是首选,为什么?

1 个答案:

答案 0 :(得分:4)

1。需要

你是对的,你想要的默认值几乎总是require(与provide配对)。这些表格与Racket的modules密切相关,可以让您更轻松地确定哪些变量应该放在哪些文件中。例如,以下文件定义了三个变量,但只导出2。

#lang racket   ; a.rkt
(provide b c)
(define a 1)
(define b 2)
(define c 3)

根据the Racket style guide,提供理想情况下应该是#lang之后文件中的第一个表单,以便您可以轻松地告诉模块提供的内容。在某些情况下,这是不可能的,但在您开始制作自己想要公开发布的Racket库之前,您可能不会遇到这些问题。就我个人而言,我仍然会在require之前放置一个文件provide,但我确实有时会感到沮丧。

在repl或其他模块中,您现在可以要求此文件并查看它提供的变量。

Welcome to Racket v6.12.
> (require "a.rkt")
> c
3
> b
2
> a
; a: undefined;
;  cannot reference undefined identifier
; [,bt for context]

ways to get around this,但这可以作为模块传达其显式导出内容的方式。

2。负载

这是一个更加动态的需求变体。通常,您不应该使用它,而是在需要动态加载模块时使用dynamic-require。在这种情况下,load实际上是require在幕后使用的原语。如果你明确地想要模仿顶级(很明显,你几乎从不这样做),那么加载是一个很好的选择。虽然在极少数情况下,我仍会指导您使用racket/load语言。这与每个表单直接输入到repl中的方式完全相互作用。

#lang racket/load
(define x 5)
(displayln x) ; => prints 5
(define x 6)
(displayln x) ; => prints 6

3。包括

包含类似于C中的#include。您应该使用它的情况更少。 include表单获取给定路径的s表达式语法,并将其直接放在include表单所在的文件中。首先,这可以作为一个很好的解决方案,允许您将单个模块拆分为多个文件,或者有一个模块'你想要放入多个文件。然而,有更好的方法可以在不使用include的情况下完成这两件事,也不会带来令人困惑的副作用。 1 有一点要记住,如果您仍然坚持使用import,您导入的文件可能不应该有#lang行,除非您明确要嵌入子模块。 (在这种情况下,除了require之外,您还需要有include表单。

4。导入

最后,import实际上并不是Racket的核心部分,而是unit system的一部分。单元在某些方面类似于模块,但允许循环依赖(单元A可以取决于单元B,而单元B取决于单元A)。近年来,由于他们的语法开销,他们已经失宠了。

与其他表单import(以及export)不同,请使用signatures,并依赖外部linker来决定哪些实际单位应该链接在一起。单位本身是一个复杂的主题,值得他们自己提出如何创建和链接它们的问题。

结论(tldr)

TLDR;使用requireprovide。它们是最受支持且最容易理解的。其他形式确实有其用途,但应考虑到“高级用途”和“高级用途”。仅

1 这些副作用与您对C中#include的期望相同。例如,顺序很重要,并且表达式也以非常不可预测的方式混合。