Failed to import record names

I don’t understand the namespacing of symbols referring to records (defined by defrecord ). I have a record defined in a namespace as follows:

(ns clojure-rte.bdd
  "Definition of Bdd."
  (:require [ :refer [cl-cond]]
            [clojure-rte.util :refer [fixed-point member group-by-mapped print-vals]]
            [clojure-rte.type :as ty]
            [clojure.set :refer [union difference intersection]]
(defrecord Bdd
  [label positive negative])

In that namespace, I can evaluate the symbol Bdd at the repl to get an object of type java.lang.Class. That’s great so far.

clojure-rte.bdd-test> (in-ns 'clojure-rte.bdd)
clojure-rte.bdd> Bdd
clojure-rte.bdd> (type Bdd)

But in another ns defined as follows:

(ns clojure-rte.bdd-test
  (:require [clojure-rte.bdd :refer :all]
            [clojure.test :refer :all]))

When I try to evaluate Bdd at the repl, it has no resolvable value.

clojure-rte.bdd> (in-ns 'clojure-rte.bdd-test)
clojure-rte.bdd-test> (resolve 'Bdd)
clojure-rte.bdd-test> Bdd
Syntax error compiling at (clojure-rte:localhost:61915(clj)*:1:15115).
Unable to resolve symbol: Bdd in this context

However, the fully qualified name is resolvable

clojure-rte.bdd-test> clojure_rte.bdd.Bdd
clojure-rte.bdd-test> (type clojure_rte.bdd.Bdd)

Perhaps it is the case that (ns clojure-rte.bdd-test (:require [clojure-rte.bdd :refer :all])) does not import record names into the namespace?

I think they need to be imported like you would a Java class.

and how would I do that if it is defined in a clojure ns?

This though is just what you see with any non-public symbol in a :require'd namespace, so presumably the classname of a defrecord is private? This wouldn’t be surprising as it lives in java land - the :refer :all does net you the factory function symbol (->classname), which is what is typically needed.

You need

(:import [clojure_rte.bdd Bdd])
1 Like

I changed my ns definition to the following, and now Bdd evaluates at the repl.

;; this is WRONG, see post below
(ns clojure-rte.bdd-test
  (:import [clojure_rte.bdd Bdd])
  (:require [clojure-rte.bdd :refer :all ]
            [clojure.pprint :refer [cl-format]]
            [clojure.test :refer :all]))

See the corrected code below.

In general, you should stick to the auto-generated constructor names and just require the namespace containing the record definition ->Bdd and map->Bdd. That way you can avoid the Java implementation details bleeding into other namespaces (that it is a class name and needs to be import'd).

@seancorfield I’m curious how would you spec a function that expects a record of a certain type then ?
I guess I could use a spec that doesn’t care if it’s a record or not and specs the map behind but if I really want to be sure it’s an instance of a certain record, I didn’t find a way without importing the related java class … Any recommandations on this ? Is this bad practice ?

1 Like

It’s hard to remember exactly which things worked and which failed, but during debugging I tried lots of combinations. At some places I wanted to assert the type Bdd, especially in transit when trying to pinpoint the function which was misbehaving. In some cases the parser complains that Bdd is not a recognized var. (assert (instance? Bdd my-value)). It is bizarre to me that what seems like an innocent symbol is not well behaved.

Actually i discovered that this ns declaration is wrong. I get an error when running tests from lein at the shell.

[geminiani:~/Repos/clojure-rte] jimka% lein test
Exception in thread "main" Syntax error compiling at (clojure_rte/bdd_test.clj:1:1).
	at clojure.lang.Compiler.load(
	at clojure.lang.RT.loadResourceScript(
	at clojure.lang.RT.loadResourceScript(
	at clojure.lang.RT.load(
	at clojure.lang.RT.load(
	at clojure.core$load$fn__6824.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
Caused by: java.lang.ClassNotFoundException: clojure_rte.bdd.Bdd
	at java.base/
	at clojure.lang.DynamicClassLoader.findClass(
	at java.base/java.lang.ClassLoader.loadClass(

The problem seems to be that we cannot :import Bdd before :require clojure-rte.bdd. I.e. first :require the package that defines it, then :import the class name.

I’ve changed the name space declaration to the following and the file seems to load properly.

(ns clojure-rte.bdd-test
  (:require [clojure-rte.bdd :refer :all ]
            [clojure.pprint :refer [cl-format]]
            [clojure.test :refer :all])
  ;; this imports the name of the Bdd record, which is otherwise not imported by :require
  (:import [clojure_rte.bdd Bdd]))

Yup. Requiring the namespace causes it to be loaded and compiled, and it that process that defines the record type, and only after that can you reference the class type (you can reference it using the fully-qualified class name if you want – import just creates a shorthand alias for it, so you can use it without the package name).

I think that’s over-specification. I would prefer to spec the keys of the map that the function requires (especially when you look at what is coming in Spec 2).

1 Like

I do wonder what the motivation here is. Why isn’t the class name included by [clojure-rte.bdd :refer :all] ? Was that just a bug in the original implementation of defrecord and how must be maintained for backward compatibility in a less-that-perfect world? Or was it intentional? To me it seems strange if intentional that all symbols except record names be exported?

When I take a look at the documentation, I see the following sentence is two locations: the defrecord name must be fully qualified. What does this mean? It seems to be stated a bit vague. For example it is not necessary to fully qualify it in all cases, case in point, its declaration. An example in the documentation at that point would be really helpful.

Also what does it mean that a name be fully qualified? I’m not 100% comfortable with the namespace naming concept especially in the case of projects with lots of levels of hierarchy? For the moment my project hierarchy is pretty simple.

defrecord generates a Java class – that is just like any other Java class you use in Clojure: once it is on the classpath, you can refer to it directly via its fully-qualified name, e.g., java.util.Date, java.time.Instant, clojure_rte.bdd.Bdd.

defrecord also generates two Clojure functions that act as constructors and which are the preferred way to create instances of the record (class): ->RecordName and map->RecordName. Since these are regular Clojure functions, you gain access to them via require – just like any other Clojure function you use.

What import does – all it does – is to allow you to refer to a Java class name by just the class name without the package name:

(import '(java.util Date))

(def now (Date.)) ; can use unqualified class as a shorthand for java.util.Date

(import '(clojure_rte.bdd Bdd))

(instance? Bdd now) ; can use unqualified class as a shorthand for clojure_rte.Bdd

There’s nothing “magical” about defrecord. When it is executed (i.e., when the namespace containing it is loaded and compiled), it generates a Java class and two Clojure functions. Everything else about it falls out of that – consistently with how Clojure deals with Java classes and Clojure functions in namespaces.

Good explanation. Additional question. when can I use (instance? Date my-object) ?
I’ve referring to a conversation on clojurians here.
If I import one of my namespaces, using :as dfa, what does the syntax dfa/xyzzy mean?
What is confusing is that if a record named Dfa has been created in that ns, then I cannot reference it with dfa/Dfa.

Java classes and Clojure functions are two separate things. A record is a Java class (with two generated Clojure functions). That is the entire explanation of why you can’t do what you’re trying to do. I just read over the Slack conversation and it’s the exact same thing I said in my post above: you’re expecting Java classes to work like namespaces or like Clojure functions and they are two different things!

(instance? java.util.Date my-object) ; will work without import

(instance? Date my-object) ; will work only after you import java.util Date

(instance? clojure_rte.bdd.Bdd my-object) ; will work after the record has been defined**

(instance? Bdd my-object) ; will work only after you import clojure_rte.bdd Bdd

** The record is defined when its namespace is loaded/compiled, which means that namespace has to be require'd somewhere before you use the class – the qualified name is available everywhere after something has require'd that namespace; the shorthand is available wherever you’ve import'd the package/class name, which in turn depends on the namespace having been require'd somewhere prior.

dfa/Dfa does not work because you are trying to mix two separate things: a namespace (alias dfa) and a Java class (Dfa). require doesn’t affect Java class access (beyond actually causing the defrecord to create the class). import doesn’t depend on namespaces – it’s only about Java class names.

Thanks for the enlightening and patient explanation, but there’s still something mysterious. One the 1 hand, perhaps it is not necessary to really understand at my state of development, but rather just form good habits. But on the other hand this mystery is troubling.

Part of your explanation, @seancorfield, is confusing to me because Dfa is not a java class (as you claim), it is a symbol used as an identifier to denote a java class, in the same way that my-object is not a function, but rather a symbol used as an identifier which designates some value. The namespace (as I understand) does not manage objects and functions and classes, but it only manages symbols. Am I right, or am I confused? The name spaces just manage which symbols can be abbreviated with short names. But the name spaces does NOT know about which values symbols have.

Does the namespace treat symbols differently depending on what type their value is? The explanation you gave above seems to claim that symbols whose values are java classes are treated differently in the namespace than symbols whose values are function or strings or numbers. This is indeed curious to me because the symbol enters the namespace after the parser encounters it, well before the evaluator determines the value of the symbol in the current evaluation context.

I think this really boils down to the fact that name spaces in clojure do not work exactly the same as they do in Common Lisp, and symbols don’t work the same either. In Common Lisp there’s no distinction between symbol and var— there are only symbols, interned and uninterned.

I think I have the same problem when I use backquote/tilde, especially when I use them outside a macro definition. I’m always in fear that some symbol references something nefarious. I’ve posed a question here, regarding my confusion about backquote.

You are right that I was deliberately omitting some details of symbols and bindings etc, but the fundamental point that I think you’re missing here is that while namespaces handle mappings from symbols to Vars (and then to values or functions), Java class names are completely separate and live in Java packages.

So you have your clojure-rte.bdd namespace containing defrecord and when it is loaded (compiled), it generates three things:

  • A Clojure function whose (symbol) name is clojure-rte.bdd/->Bdd – mapped in the clojure-rte.bdd namespace
  • A Clojure function whose (symbol) name is clojure-rte.bdd/map->Bdd – mapped in the clojure-rte.bdd namespace
  • A Java class whose (symbol) name is clojure_rte.bdd.Bdd – which “lives” in the Java package clojure_rte.bdd

Even though at compilation time, Clojure sees just the symbol Bdd, it will either resolve it to a Clojure binding or Var through the available namespaces or resolve it to a Java class name through the available Java packages, based either on shortcuts introduced via import if it is unqualified or as a fully-qualified Java class name if it is qualified.

Those are two completely separate spaces. I’m not sure I can make it any clearer than that so I hope that completely answers your questions?


@Jim_Newton I wonder if this REPL session helps in terms of seeing the two separate spaces that Clojure names and Java (class) names live in:

user=> javax.sql.DataSource ; fully-qualified class name
javax.sql.DataSource ; resolves to itself
user=> (ns javax.sql)
javax.sql=> (def DataSource 42)
javax.sql=> DataSource ; looks up symbol in current ns and finds it
javax.sql=> (in-ns 'user)
#object[clojure.lang.Namespace 0x72458efc "user"]
user=> javax.sql.DataSource ; still refers to fully-qualified class name
user=> javax.sql/DataSource ; note the Clojure qualified syntax
user=> (require '[javax.sql :as q])
user=> q/DataSource ; Clojure qualification, resolves to value of Clojure Var in javax.sql ns
user=> (import '(javax.sql DataSource)) ; create local alias for javax.sql.DataSource
user=> DataSource ; unqualified _class_ name resolves to fully-qualified class name

Now moving into unqualified Clojure name lookup:

user=> (require '[javax.sql :refer [DataSource]]) ; refer in the Clojure DataSource symbol...
;; ...see that we get a warning about conflict with the DataSource shorthand from import above:
WARNING: DataSource already refers to: interface javax.sql.DataSource in namespace: user, being replaced by: #'javax.sql/DataSource
user=> DataSource ; unqualified name resolves to referred Clojure Var from javax.sql ns
user=> javax.sql.DataSource ; can still use fully-qualified Java class name
user=> javax.sql/DataSource ; can still use fully-qualified Clojure symbol
1 Like