【问题标题】:Why multiple namespaces?为什么要使用多个命名空间?
【发布时间】:2011-03-20 16:50:19
【问题描述】:

在 Common Lisp 中为值和函数设置单独的命名空间的设计决策背后的基本原理是什么?支持和反对的论据是什么?

【问题讨论】:

    标签: namespaces lisp scheme common-lisp


    【解决方案1】:

    除了上面提到的其他问题之外,为函数提供单独的命名空间使得 CL 不卫生的宏不太可能咬住宏用户。在 CL 中,出现在宏扩展中的调用点处绑定的名称将具有在调用点使用的定义,而不是在定义宏时使用的定义。因此,在 Lisp-1 版本的 CL 中,如果宏扩展为对 LIST 函数的调用,并且 LIST 在调用宏时被定义为变量,则宏会发生故障。 (请注意,gensyms 不能解决这个问题,不像他们解决的逆问题。)

    这在Scheme中不会发生,因为默认情况下Scheme宏是卫生的:宏扩展中使用的所有名称都具有它们在定义宏时的含义,而不是使用它的位置。

    【讨论】:

      【解决方案2】:

      Common Lisp 基本上是原始 Lisp 1.5 的后代,或者更确切地说,是其不同方言的统一。最初的 Lisp 1.5 就是现在所说的 Lisp-2。因为那是在 60 年代,你可以将函数传递给其他函数的事实已经够奇怪了。甚至没有人会想到让它们共享同一个命名空间。今天发明的几乎所有支持高阶函数和匿名函数的语言都选择单一命名空间方法。包括 Clojure,它在其他方面更接近于 Common Lisp 而不是 Scheme。

      Scheme 和 Clojure 一样,最初并不是与 Lisp 1.5 不同的方言,对于它们的目的来说它是有意义的。

      当然,在 Clojure 中,向量、散列映射、集合和所有东西也可以应用于参数,所以从某种意义上说,Clojure 中的向量可以看作是一个函数,它接受一个自然数并从中产生一个值.

      【讨论】:

      • (可能还值得一提的是,非常古老的 lisp 中的函数类似于 Emacs Lisp 中的函数——只是一个以lambda 开头的列表。)
      • 真的吗?我不知道。所以他们只是在应用之前更改了 eval 中低级别的绑定变量?该死的,那一定很贵。
      • Rich Hicky 谈到了他如何避免 Lisp-1 中常见的一些陷阱。 (在 blib.tv 上查找 clojure)。我记不清了,所以你想自己解释一下。
      【解决方案3】:

      虽然理论上每种方式都可能有很多论据,但我敢打赌,这在很大程度上是哲学的起源。 Scheme 是一个 Lisp-1,它更喜欢优雅而不是实用性,并且为变量和函数选择了相同的 define 语法,这使得单个命名空间感觉自然(并鼓励函数式编程风格)。 Common Lisp 倾向于实用性和强大而不是优雅,并且是一种建立共识的尝试,因此看到现有的两个命名空间解决方案被广泛接受并且运行良好,就接受了它。

      然而,在实践中,它主要意味着三件事:

      • 在 Common Lisp(和其他 Lisp-2)中,您必须经常使用 funcall
      • 在 Scheme(和其他 Lisp-1)中,您必须小心不要覆盖 需要带变量的函数名;例如函数参数如 lst 而不是 list
      • 网上会有争论

      然而,这是为什么有些人更喜欢一种 Lisp 而不是另一种的主要因素。

      【讨论】:

      • 差异的结果比仅仅使用funcall 或避免名称冲突更深:在Scheme 中使用高阶函数更自然,因此更惯用,因此编译器会努力优化它。但是,在 CL 中,如果您使用 defvar 定义变量,然后使用 funcall 定义变量,那么编译器很可能会将其编译为比使用函数定义慢得多的代码。这类似于 CLers 更喜欢循环构造,而方案者更喜欢尾调用。但是当然你的第三点是最重要的...
      • 是的,这也是我所说的“鼓励函数式编程风格”的一部分——感谢您让它更明确。
      【解决方案4】:

      我实际上喜欢拥有多个命名空间(甚至超过两个);它使用户和编译器编写者(实现)更容易:

      CL-USER> (defclass test () ())
      #<STANDARD-CLASS TEST>
      CL-USER> (defun test ())
      TEST
      CL-USER> (defparameter test 42)
      TEST
      
      CL-USER> (describe 'test)
      COMMON-LISP-USER::TEST
        [symbol]
      
      TEST names a special variable:
        Value: 42
      
      TEST names a compiled function:
        Lambda-list: ()
        Derived type: (FUNCTION NIL (VALUES NULL &OPTIONAL))
        Source form:
          (LAMBDA ()
            (DECLARE (MUFFLE-CONDITIONS COMPILER-NOTE))
            (PROGN
             (SB-INT:NAMED-LAMBDA TEST
                 NIL
               (BLOCK TEST))))
      
      TEST names the standard-class #<STANDARD-CLASS TEST>:
        Direct superclasses: STANDARD-OBJECT
        No subclasses.
        Not yet finalized.
        No direct slots.
      
      ; No value
      
      CL-USER> (make-instance 'test)
      #<TEST {1005B1D601}>
      CL-USER> (test)
      NIL
      CL-USER> test
      42
      CL-USER> 
      

      【讨论】:

      • 你能解释一下为什么你认为“它让用户更容易”吗?我写的 Scheme 不多,但是我写了相当多的 Python,并且从来没有遇到过想要给类、实例和/或方法同名的问题。就此而言,即使在我可以重用这样的名称的 Common Lisp 中,我想我也从来没有。实例往往是名词,功能往往是动词等。
      • @Ken:我经常在 CL 中编写 (defun foo (list) (list (bar list))) 之类的东西,这不适用于单个命名空间。必须在 Scheme 中为我的论点命名,例如 lystlst,这让我有点抓狂。
      • Pillsy:确实如此。再说一次,调用参数list 也会让我有点发疯,因为它是如此非描述性的。我称它为sourcetargethaystackwords,这说明了列表的目的是什么。如果我按类型调用参数,我 90% 的参数将是 list。 :-)
      • 老实说,“list-as-parameter”参数最终让我相信 Lisp-2 是不必要的,因为完全不合逻辑的原因:这种情况完全难以置信 i> (当人们重复使用同一个令人难以置信的例子时,会让人觉得立场很弱)。
      【解决方案5】:

      请参阅 Richard P. Gabriel 的论文 Technical Issues of Separation in Function Cells and Value Cells,了解有关该主题的完整学术处理。

      【讨论】:

        猜你喜欢
        • 2020-11-24
        • 2015-02-10
        • 1970-01-01
        • 2020-08-07
        • 1970-01-01
        • 2019-05-12
        • 2016-12-10
        • 2014-09-22
        • 2013-05-10
        相关资源
        最近更新 更多