Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use a type outside of its own namespace in clojure?

I have a project set up with leiningen called techne. I created a module called scrub with a type in it called Scrub and a function called 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)))) 

When I run the test, I get the error:

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)

If I remove test-scrub everything works fine. Why does :use techne.scrub 'import' the function definitions but not the type definitions? How do I reference the type definitions?

like image 950
Nick Orton Avatar asked Sep 11 '10 11:09

Nick Orton


2 Answers

I add the import, but get the same problem. I'm testing with the Expectations package 2.0.9, trying to import deftype Node and interface INode.

In 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) _))

In 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]))

and the output from 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

The suggestion to use a factory method, however, is a viable work-around.

like image 129
Bob Stine Avatar answered Sep 28 '22 17:09

Bob Stine


Because deftype generates a class, you will probably need to import that Java class in techne.scrub-test with (:import [techne.scrub Scrub]) in your ns definition.

I actually wrote up this same thing with respect to defrecord here:

  • http://tech.puredanger.com/2010/06/30/using-records-from-a-different-namespace-in-clojure/

Another thing you could do would be to define a constructor function in scrub:

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

and then you would not need to import Scrub in test-scrub.

like image 39
Alex Miller Avatar answered Sep 28 '22 17:09

Alex Miller