【问题标题】:How do you use a type outside of its own namespace in clojure?如何在 clojure 中使用自己命名空间之外的类型?
【发布时间】:2011-04-11 02:03:26
【问题描述】:

我用 leiningen 建立了一个名为 techne 的项目。我创建了一个名为 Scrub 的模块,其中包含一个名为 Scrub 的类型和一个名为 foo 的函数。

techne/scrub.clj:

(ns techne.scrub)
  (deftype Scrub [state]
    Object
     (toString [this]
     (str "SCRUB: " state)))

(defn foo
  [item]
  (Scrub. "foo")
  "bar")

techne/scrub_test.clj:

(ns techne.scrub-test                                                                                                                                             
  (:use [techne.scrub] :reload-all)                                                                                                                               
  (:use [clojure.test]))                                                                                                                                          


(deftest test-foo                                                                                                                                                 
  (is (= "bar" (foo "foo"))))                                                                                                                                                           

(deftest test-scrub                                                                                                                                               
  (is (= (Scrub. :a) (Scrub. :a)))) 

当我运行测试时,我得到了错误:

Exception in thread "main" java.lang.IllegalArgumentException: Unable to resolve    classname: Scrub (scrub_test.clj:11)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:5376)
    at clojure.lang.Compiler.analyze(Compiler.java:5190)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:5357)

如果我删除测试擦洗一切正常。为什么 :use techne.scrub “导入”函数定义而不是类型定义?如何引用类型定义?

【问题讨论】:

    标签: testing clojure leiningen deftype


    【解决方案1】:

    因为deftype 生成一个类,您可能需要在您的ns 定义中使用(:import [techne.scrub Scrub]) 在techne.scrub-test 中导入该Java 类。

    我实际上在这里就 defrecord 写了同样的事情:

    你可以做的另一件事是在scrub中定义一个构造函数:

    (defn new-scrub [state] 
      (Scrub. state))
    

    然后你就不需要在 test-scrub 中导入 Scrub。

    【讨论】:

    • 出于这个原因和验证,我总是使用构造函数。
    • 是的,我们发现扩展 defrecord 以自动添加具有字段验证的构造函数、对可评估表单的 pprint 支持等很有帮助。
    • 请注意,此答案早于 Clojure 1.4。从 1.4 开始,defrecord 将自动创建位置 (->Scrub) 和地图 (map->Scrub) 构造函数。这是首选的构造方法,只需要您将这些函数引用到您的命名空间中 - 无需导入类。
    【解决方案2】:

    我添加了导入,但遇到了同样的问题。我正在使用 Expectations 包 2.0.9 进行测试,尝试导入 deftype Node 和接口 INode。

    在 core.clj 中:

    (ns linked-list.core)
    
    (definterface INode
      (getCar [])
      (getCdr [])
      (setCar [x])
      (setCdr [x]))
    
    (deftype Node [^:volatile-mutable car ^:volatile-mutable cdr]
      INode
      (getCar[_] car)
      (getCdr[_] cdr)
      (setCar[_ x] (set! car x) _)
      (setCdr[_ x] (set! cdr x) _))
    

    在 core_test.clj 中:

    (ns linked-list.core-test
      (:require [expectations :refer :all]
                [linked-list.core :refer :all])
      (:import [linked-list.core INode]
               [linked-list.core Node]))
    

    以及 lein autoexpect 的输出:

    *************** Running tests ***************
    Error refreshing environment: java.lang.ClassNotFoundException: linked-list.core.INode, compiling:(linked_list/core_test.clj:1:1)
    Tests completed at 07:29:36.252
    

    但是,使用工厂方法的建议是一种可行的解决方法。

    【讨论】:

    • 我相当肯定,与 :require 不同,当您执行 :import 时,Clojure 不会修改路径名。因此,对于所有导入,“linked-list.core Node”应该是“linked_list.core Node”。我使用 Cursive+IntelliJ,它总是以这种方式自动导入。
    猜你喜欢
    • 2018-10-25
    • 2019-01-18
    • 2015-01-21
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    • 2011-08-31
    • 2013-03-12
    • 1970-01-01
    相关资源
    最近更新 更多