【问题标题】:Separate Namespaces for Functions and Variables in Common Lisp versus SchemeCommon Lisp 与 Scheme 中函数和变量的独立命名空间
【发布时间】:2010-11-04 11:49:05
【问题描述】:

Scheme 为所有变量使用一个命名空间,无论它们是否绑定到函数或其他类型的值。 Common Lisp 将两者分开,因此标识符“hello”可以在一个上下文中引用一个函数,而在另一个上下文中引用一个字符串。

(注1:这个问题需要上面的一个例子;随意编辑它并添加一个,或者给原作者发电子邮件,我会这样做。)

但是,在某些情况下,例如将函数作为参数传递给其他函数,程序员必须使用#' 明确区分他指定的是函数变量,而不是非函数变量,如下所示:

(sort (list '(9 A) '(3 B) '(4 C)) #'

我一直认为这有点小问题,但我最近遇到了argument,说这实际上是一个功能:

...这 重要的区别实际上在于形式的语法,而不是 对象的类型。在不了解运行时值的情况下 很明显,函数形式的第一个元素 必须是一个函数。 CL 接受了这一事实,并将其作为 语言,以及宏和特殊形式,它们也可以(而且必须) 静态确定。所以我的问题是:你为什么要 函数名和变量名要相同 命名空间,当函数名的主要用途是出现在 变量名很少想出现?
考虑类名的情况:为什么一个名为 FOO 的类要阻止 使用名为 FOO 的变量?唯一一次我会提到 名为 FOO 的类在需要类名的上下文中。如果,在 极少数情况下我需要获取绑定到的类对象 类名 FOO,有 FIND-CLASS。

从经验来看,这个论点对我来说确实有些道理; Haskell 中也有类似的情况,字段名称也是用于访问字段的函数。这有点尴尬:

data Point = Point { x, y :: Double {- lots of other fields as well --} }
isOrigin p = (x p == 0) && (y p == 0)

这可以通过一些额外的语法来解决,NamedFieldPuns 扩展特别好:

isOrigin2 Point{x,y} = (x == 0) && (y == 0)

那么,对于这个问题,除了一致性之外,Common Lisp 与 Scheme 以及一般而言,所有值的单个命名空间与函数和非函数值的单独命名空间相比有哪些优点和缺点?

【问题讨论】:

  • “Lisp 与 Scheme”有点错误。 Lisp 是编程语言家族,Scheme 是 Lisp 的一种方言。 Common Lisp、Emacs Lisp、ISLisp、AutoLisp、Clojure 等也是 Lisp 家族的成员。有几种 Lisp 方言具有单独的命名空间(用于函数和变量),而另一些则没有。例如,新的 Lisp 方言 Clojure 就没有。 ISLisp(一种 ISO 标准化的 Lisp 方言)可以。
  • 你是对的。我已更新帖子以使用“Common Lisp”而不仅仅是“Lisp”。

标签: variables scheme lisp common-lisp lisp-2


【解决方案1】:

我看到的最大缺点是可理解性,至少对于 Common Lisp 而言是这样。我们都同意它为变量和函数使用不同的命名空间,但它有多少?在 PAIP 中,Norvig 表明它有“至少七个”命名空间。

当语言的经典书籍之一,由一位备受尊敬的程序员撰写,在出版的书中甚至不能肯定地说,我认为有问题。我对多个命名空间没有问题,但我希望语言至少足够简单,有人 可以完全理解它的这一方面。

我很乐意为变量和函数使用相同的符号,但在更晦涩的领域,我出于恐惧而使用不同的名称(冲突的命名空间真的很难调试!),这确实应该从来都不是这样。

【讨论】:

    【解决方案2】:

    这两种方法都有好处。但是,我发现在重要的时候,我更喜欢同时拥有一个函数 LIST 和一个变量 LIST 而不是拼错其中一个。

    【讨论】:

      【解决方案3】:

      实际上,正如the paper by Richard Gabriel and Kent Pitman 中所述,争论是关于 Lisp-5 与 Lisp-6 的,因为已经存在其他几个命名空间,在论文中提到了类型名称、标记名称、块名称和声明名称. 编辑:这似乎是不正确的,正如 Rainer 在评论中指出的那样:Scheme 实际上似乎是一个 Lisp-1。不过,以下内容在很大程度上不受此错误的影响。

      一个符号是表示要执行的东西还是要引用的东西总是从上下文中清楚的。将函数和变量放入同一个命名空间主要是一种限制:程序员不能对事物和动作使用相同的名称。 Lisp-5 从中得到的只是避免了从不同于当前上下文所暗示的命名空间中引用某些东西的一些语法开销。 编辑:这不是全貌,只是表面。

      我知道 Lisp-5 的支持者喜欢函数是数据这一事实,并且这在语言核心中得到了表达。我喜欢这样一个事实,即我可以将列表称为“列表”并将汽车称为“汽车”,而不会混淆我的编译器,而且函数无论如何都是一种从根本上特殊的数据。 编辑:这是我的主要观点:单独的命名空间根本不是一个缺点。

      我也喜欢 what Pascal Constanza had to say 关于这个。

      【讨论】:

      • 一般性辩论是关于 Lisp-1 与 Lisp-n 的对比。 Scheme 中的想法是一些东西是一流的对象,并且有一个单一的命名机制。以 Common Lisp 中非本地控制传输的标签、块名称等为例——它们由类似于它们自己的命名空间中的标识符表示。 Scheme 使用的是第一类对象的延续,它们没有自己的命名空间。我也更喜欢 Lisp-n,但承认 Lisp-1 背后的设计原则:让一切都成为“一流”的价值并提供单一的命名机制。
      • 呵呵,我一直以为Scheme和Lisp比较接近,不知道它没有标签和块。无论如何,我也无意以任何方式推迟 Scheme,只是说明我的偏好,在我看来,拥有单独的命名空间根本不是一个缺点。
      • 我不清楚你的“总是从上下文中清除”的话。你是说当我评估(map f l) 时,它将使用f 的函数命名空间绑定,而不是变量命名空间绑定?还是我必须评估(map #'f l),在这种情况下,由于程序员必须明确消除歧义,因此从上下文中不清楚?
      • s 表达式的第一个元素是要执行的东西,默认情况下取自与该符号关联的函数。以下所有元素都是要传递给已执行事物的事物,默认情况下,这些元素取自与各个符号关联的值。如果您评估(map f l),则与map 关联的函数将应用于与fl 关联的值是完全明确的。 “上下文”在这里是指“词汇上下文”。 "#'" 不会消除歧义,它只是覆盖默认值。
      • 小修正:第一个元素命名函数,正确。但它不是取自与符号关联的函数。它取自与函数命名空间中的名称在词法上关联的函数。如果函数命名空间中没有词法绑定函数,则使用符号的函数值。 #'foo 或 (FUNCCTION foo) 是一种访问函数命名空间并将函数对象作为值检索的方法。如果该名称下没有词法绑定函数,则返回符号的函数值。对于词法绑定的名称,不涉及任何符号。
      【解决方案4】:

      Scheme 中的函数名只是一个以函数为值的变量。无论是(define x (y) (z y)) 还是(let ((x (lambda (y) (z y)))),我都在定义一个可以调用的函数。因此,就 Scheme 而言,“变量名很少希望出现在那里”的想法有点似是而非。

      Scheme 是一种典型的函数式语言,因此将函数视为数据是其原则之一。让函数成为自己的一种类型,并像所有其他数据一样存储是实现这一想法的一种方式。

      【讨论】:

        【解决方案5】:

        这两种不同的方法有名称:Lisp-1 和 Lisp-2。 Lisp-1 对变量和函数都有一个命名空间(如在 Scheme 中),而 Lisp-2 对变量和函数有单独的命名空间(如在 Common Lisp 中)。我提到这一点是因为您可能不知道该术语,因为您没有在问题中提及它。

        维基百科refers to this debate:

        单独的函数命名空间是否是一种优势是 Lisp 社区中争论的根源。它通常被称为 Lisp-1 与 Lisp-2 的辩论。 Lisp-1 指的是 Scheme 的模型,Lisp-2 指的是 Common Lisp 的模型。这些名称是由 Richard P. Gabriel 和 Kent Pitman 于 1988 年在一篇论文中创造的,该论文广泛比较了这两种方法。

        Gabriel 和 Pitman 的题为 Technical Issues of Separation in Function Cells and Value Cells 的论文解决了这个问题。

        【讨论】:

          【解决方案6】:

          我在 Python(统一命名空间)与 Ruby(方法与非方法的不同命名空间)中遇到了类似的区别。在这种情况下,我更喜欢 Python 的方法——例如,使用这种方法,如果我想列出一些东西,其中一些是函数,而另一些不是,我不必对它们的名称做任何不同的事情,例如,取决于它们的“功能性”。类似的考虑适用于函数对象被捆绑而不是调用的所有情况(高阶函数的参数和返回值等)。

          也可以调用非函数(如果它们的类定义 __call__,在 Python 的情况下——“运算符重载”的一种特殊情况),因此“上下文区别”也不一定清楚。

          但是,我的“lisp-oid”体验主要是/曾经是使用 Scheme 而不是 Common Lisp,所以我可能会潜意识地对最终来自该体验的统一命名空间的熟悉程度产生偏见。

          【讨论】:

          • 你可以用 Ruby 中的方法或多或少地做同样的事情:f = obj.method(:foo) 会给你一个Method 对象,这是一个完全正常的对象。您可以使用f.call(...) 在对象的上下文中调用该方法。然而,这并不是说函数在 Ruby 中是真正一流的,尽管我不想深入了解所有蹩脚的细节。
          猜你喜欢
          • 1970-01-01
          • 2023-03-29
          • 1970-01-01
          • 1970-01-01
          • 2017-04-20
          • 2012-06-28
          • 1970-01-01
          • 2022-10-25
          • 1970-01-01
          相关资源
          最近更新 更多