Namespace loading with defrecord and attempting to call unbound fn

382 views
Skip to first unread message

Pierre Allix

unread,
Apr 15, 2013, 5:46:21 AM4/15/13
to Clojure
Hello,

When defining a record with defrecord and calling from Java a method
of the record which is implemented by calling the function of another
namespace, the exception IllegalStateException is thrown with error "
attempting to call unbound fn".

An example of this could be something like that:

(ns mynamespace
(:require [some-stuff :as s])

(defrecord MyRecord
[]
MyInterface
(doSomething [this]
(s/do-something)))

Similar errors are referenced here:

https://groups.google.com/forum/#!msg/clojure-dev/4CtSVWcD15A/shpMuyjMpxsJ

http://stackoverflow.com/questions/10953621/clojure-deftype-calling-function-in-the-same-namespace-throws-java-lang-illegal


A workaround is to use a (require) on the first instruction of the
doSomething method.

For me it looks like a bug and it makes it more difficult than
necessary to interface Clojure from Java for my coworkers.

I'm surprised that this behaviour is still in Clojure 1.5.

Is there some plan to fix that or is "by design" so?

Meikel Brandmeyer (kotarak)

unread,
Apr 15, 2013, 7:33:48 AM4/15/13
to clo...@googlegroups.com
Hi,

reading the resources you linked, I got the impression that just the initial require is missing. Instead of adding the require to the method, it should be added to the application init (or some appropriate static initializer? The class giving out instances of the record might be a candidate.). That should be sufficient. However there was no complete code demonstrating the issue. Only the clojure side. So I may be missing something.

Behaviour like for gen-class is not implemented, yet, AFAIK.

Meikel

Pierre Allix

unread,
Apr 15, 2013, 8:30:09 AM4/15/13
to Clojure
Hi Meikel,

If you look at the source of this project:

git clone git://git.berlios.de/markos-license-analyser

in the test directory there is a JUnit test where the Clojure record
is instantiated through a normal Java 'new' call.
The record is imported with a normal Java import.

If you want I could create a project reproducing the bug.


On Apr 15, 1:33 pm, "Meikel Brandmeyer (kotarak)" <m...@kotka.de>
wrote:

Meikel Brandmeyer (kotarak)

unread,
Apr 15, 2013, 1:57:28 PM4/15/13
to clo...@googlegroups.com
Hi Pierre,

does this patch work?

----8<--8<--8<----
diff --git a/test/eu/markosproject/test/LicenseCheckerTest.java b/test/eu/markosproject/test/LicenseCheckerTest.java
index 2e017cc..1a1cc82 100644
--- a/test/eu/markosproject/test/LicenseCheckerTest.java
+++ b/test/eu/markosproject/test/LicenseCheckerTest.java
@@ -11,8 +11,12 @@ import eu.markosproject.commons.interfaces.licensing.ICheckerResultStorer;
 import eu.markosproject.commons.interfaces.licensing.IRNotification;
 import eu.markosproject.licensing.LicenseChecker;
 
+import clojure.lang.RT;
 
 public class LicenseCheckerTest {
+ static {
+ RT.var("clojure.core", "require").invoke(RT.var("clojure.core", "symbol").invoke("eu.markosproject.licensing"));
+ }
 
  private final static Logger log = Logger.getLogger(LicenseCheckerTest.class .getName()); 
 
----8<--8<--8<----

I'm not convinced that records are supposed to be created directly. (Ok. Testing. Maybe.) They should be an implementation detail returned by an API. If you need Java access there you might want to still go the Clojure route and provide Java access through a façade. cf. Rich Hickey's example of Datomic: http://skillsmatter.com/podcast/scala/impromptu-rich-hickey-lightning-talk

Meikel

Pierre Allix

unread,
Apr 16, 2013, 5:32:38 AM4/16/13
to clo...@googlegroups.com
Thank you the patch works.

I though it would have made sense to just create a record directly from Java since they can implement Java interfaces.

Meikel Brandmeyer (kotarak)

unread,
Apr 16, 2013, 5:47:24 AM4/16/13
to clo...@googlegroups.com
Hi,


Am Dienstag, 16. April 2013 11:32:38 UTC+2 schrieb Pierre Allix:
Thank you the patch works.

I though it would have made sense to just create a record directly from Java since they can implement Java interfaces.


As a rule of thumb, I go with the non-AOT behaviour of Clojure. In that case the record does not exist until the namespace is required. So if I want to use it (in particular directly from Java), I have to require the namespace upfront to actually create the record class. And even if AOT would behave differently (think gen-class: require implementing namespace on class init), doing it explicitly is more robust because it allows both - AOT and non-AOT - usage.

But as I said before: I would not create the record directly but go through the support functions ("->Bar", "map->Bar"). Or even higher level constructing functions. In Java this is probably the factory pattern.

Meikel

Reply all
Reply to author
Forward
0 new messages