Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I bind a defrecord in clojure?

I have something like this:

user> (defrecord vertex [id val]) => user.vertex
user> (def v vertex)              => #'user/v
user> (= v vertex)                => true
user> (type v)                    => java.lang.Class
user> (type vertex)               => java.lang.Class
user> (vertex. 1 2)               => #user.vertex{:id 1, :val 2}
user> (v. 1 2)                    => "Unable to resolve classname v"
user> (new v 1 2)                 => "Unable to resolve classname v"

So basically I can't bind vertex to another name. Same thing with trying to pass a defrecord type to a function, or let, or anything else. Why is this the case, and what can I do to temporarily rename a defrecord?

I'm guessing it's some trick having to do with Java interop.

like image 536
Sonicsmooth Avatar asked Nov 30 '25 05:11

Sonicsmooth


2 Answers

defrecord generates a Java class, which I think is actually treated as a form of special case in Clojure, in particular with regards to interop (though I'm not positive).

If your objective is to be able to easily pass around a function which can create vertexes, then the solution is to use a local function which calls the constructor rather than doing the interop yourself.

In Clojure 1.3, deftype and defrecord automatically generate two additional methods:

  • ->{type} is equivalent to the constructor
  • map->{type} takes a map of arguments as an argument

For the above, (->vertex 1 2) and (map->vertex {:id 1 :val 2}) work, and allow you to do the construction you're after.

If you really do need the class available as you pass it around, there may be something you can do with macros, though I'm not sure.

like image 54
deterb Avatar answered Dec 01 '25 18:12

deterb


This isn't possible to my knowledge.

If you think about how defrecord works; It actually byte code generates a (java) class. You can see this in action when you do a defrecord in one namespace and want to use it in another...

(ns my-first-namespace)
(defrecord Foo [x y])

(ns my-second-namespace
   (:use my-first-namespace)
   (:import my_first_namespace Foo)) ; still have to import the java class 
                                     ; underlying the defrecord even tho 
                                     ; the defining namespace is use'd (note 
                                     ; hyphen / underscore changes). 

Unfortunately there is no way in java (really in the JVM) to alias a classname to another classname. The closest you could get is to subclass to a new name, but that's fairly gross.

like image 28
sw1nn Avatar answered Dec 01 '25 18:12

sw1nn