【问题标题】:Clojure compile error for "declare" variables in deftest在deftest中“声明”变量的Clojure编译错误
【发布时间】:2013-10-22 23:00:11
【问题描述】:

我的背景包括 10 年的通用 lisp,所以现在我正在学习 Clojure,方法是编写一个符号数学包,其中包含命名空间中的向量(即 a、b、c)和 Nvector 绑定(ab、ac、bc 等),其中为这些对象定义的打印方法。

因此,当我在绑定函数所在文件的底部编写我的 deftests 时,我必须编写(声明 a b ab)以避免编译器警告(这很有意义)。

    (def G3 (doall (ga-bindall "a b c")))
    galg.core=> G3
    (+a +b +c +a*b +a*c +b*c +a*b*c +a*b +a*c +b*c +a*b*c)
    galg.core=> [a +a -a ab +ab -ab a*b +a*b -a*b abc]
    [+a +a -a +a*b +a*b -a*b +a*b +a*b -a*b a*b*c]

    (deftest galg-vectors
      (declare a b ab)   ;<=== required when in same file as definitions
      (testing "GALG Vector, Nvector and Sum tests."
        (is (= (Vector 'a) a))
        (is (= (Vector 'a -1) -a))
        (is (= ab (Nvector 'a 'b)))
        (is (= (+ 1 a ab) (Sum 1 a ab)))
        ))

然后,当我通过将测试移至 galg.core-test 文件来清理代码时,如下所示:

    (ns galg.core-test (:use clojure.test galg.core))  ;;<== imports a, b, ab, etc
    (deftest galg-vectors
      ;(declare a b ab)   ;<=== must be removed when in separate file 
      (testing "GALG Vector, Nvector and Sum tests."
        (is (= (Vector 'a) a))
        (is (= (Vector 'a -1) -a))
        (is (= ab (Nvector 'a 'b)))
        (is (= (+ 1 a ab) (Sum 1 a ab)))
        ))

...那么,当(declare a b ab)存在时,“已经引用:”编译器错误发生:

CompilerException java.lang.IllegalStateException: a 已经引用:#'galg.core/a 在命名空间:galg.core-test, compile:(NO_SOURCE_PATH:2:3)

这个错误似乎有点过分,因为根据我的想法,“声明”实际上是编译器的“绑定承诺”,没有真正定义一个。

有什么想法吗?

【问题讨论】:

    标签: clojure declare


    【解决方案1】:

    declare 不是那么聪明,它并没有真正创建以后会创建某些东西的承诺,而是现在创建它,并且它总是在本地命名空间中创建它,然后它让您确保在使用之前获得一个值,否则当您尝试将未绑定值用于需要绑定值的事物时将引发异常。 def 足够聪明,不会在之前定义的情况下用未绑定的值覆盖绑定值。

    user> (macroexpand-1 '(declare a))
    (do (def a)) 
    

    如您所见,declare declare 创建了具有未绑定值的本地 var,正是此本地 var 创建触发了您看到的错误。因为命名空间 (`ns) 表达式已经将具有该名称的条目添加到您的命名空间,所以当您的表达式:

     (declare a b ab) 
    

    运行它将扩展为:

     (do (def a) (def b) (def ab))
    

    这将尝试在名为 a 的本地命名空间中创建一个 var,这会触发错误,因为该名称已经引用了另一个命名空间。

    【讨论】:

    • 现在删除我的赞成票为时已晚,但事实证明这是错误的。你可以在一个命名空间内以任意顺序声明和定义,错误是由命名空间之间的使用语义引起的。
    • 也许错误是说得太强烈了——但不完整,并没有完全触及问题的根源
    • 我会进行编辑以使其更完整,如果您仍然觉得它有问题,这将给您一个机会来删除您的投票,尽管我宁愿改进答案,直到它变得更完整;)
    【解决方案2】:

    如果在同一个命名空间内完成,可以任意顺序定义和声明,不会出错。

    user> (def a 0)
    #'user/a
    user> (declare a)
    #'user/a
    

    问题是所有 clojure 变量都是命名空间的。声明在调用它的命名空间中创建变量。因为您已调用 use 来引用来自另一个命名空间的符号,所以声明它们会创建新变量 galg.core-test/a 等,这会影响您正在使用的变量 galg.core/a 等。错误消息告诉您一个不相关的由于use,var 正在遮蔽以前在范围内的那个。

    【讨论】:

    • 而且,在社论上,这样的事情是我避免use 甚至refer 的原因。当您在每个符号上都有一个不是来自当前命名空间或 clojure.core 的显式命名空间时,事情就会变得更加清晰。
    • 感谢所有 cmets 和讨论,因为与普通的 lisp 略有不同。 “使用”的目的是因为我使用repl来访问向量绑定的短名称(def a(Vector'a))和(def ab(Nvector'a'b))等,可以使用例如 (+ a ab)。所以不要使用完全限定的命名空间名称 glg.core/a 甚至使用:作为 g/a 的别名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 2017-06-09
    相关资源
    最近更新 更多