defrecord

clojure.core

  • (defrecord name [& fields] & opts+specs)
Alpha - subject to change

(defrecord name [fields*] options* specs*)

Currently there are no options.

Each spec consists of a protocol or interface name followed by zero
or more method bodies:

protocol-or-interface-or-Object
(methodName [args*] body)*

Dynamically generates compiled bytecode for class with the given
name, in a package with the same name as the current namespace, the
given fields, and, optionally, methods for protocols and/or
interfaces.

The class will have the (immutable) fields named by
fields, which can have type hints. Protocols/interfaces and methods
are optional. The only methods that can be supplied are those
declared in the protocols/interfaces. Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.

Method definitions take the form:

(methodname [args*] body)

The argument and return types can be hinted on the arg and
methodname symbols. If not supplied, they will be inferred, so type
hints should be reserved for disambiguation.

Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that a parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations. Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.

In the method bodies, the (unqualified) name can be used to name the
class (for calls to new, instance? etc).

The class will have implementations of several (clojure.lang)
interfaces generated automatically: IObj (metadata support) and
IPersistentMap, and all of their superinterfaces.

In addition, defrecord will define type-and-value-based =,
and will defined Java .hashCode and .equals consistent with the
contract for java.util.Map.

When AOT compiling, generates compiled bytecode for a class with the
given name (a symbol), prepends the current ns as the package, and
writes the .class file to the *compile-path* directory.

Two constructors will be defined, one taking the designated fields
followed by a metadata map (nil for none) and an extension field
map (nil for none), and one taking only the fields (using nil for
meta and extension fields).

5 Examples top

  • ;; from Stu's examples:
    
    (defrecord Person [fname lname address])
    -> user.Person
    
    (defrecord Address [street city state zip])
    -> user.Address
    
    (def stu (Person. "Stu" "Halloway"
               (Address. "200 N Mangum"
                          "Durham"
                          "NC"
                          27701)))
    -> #'user/stu
    
    (:lname stu)
    -> "Halloway"
    
    (-> stu :address :city)
    -> "Durham"
    
    (assoc stu :fname "Stuart")
    -> #:user.Person{:fname "Stuart", :lname "Halloway", :address #:user.Address{:street "200 N Mangum", :city "Durham", :state "NC", :zip 27701}}
    
    (update-in stu [:address :zip] inc)
    -> #:user.Person{:fname "Stu", :lname "Halloway", :address #:user.Address{:street "200 N Mangum", :city "Durham", :state "NC", :zip 27702}}
  • ;; This example shows how to implement a Java interface in defrecord.
    ;; We'll implement FileNameMap (because it has a simple interface, 
    ;; not for its real purpose).  
    
    (import java.net.FileNameMap)
    -> java.net.FileNameMap
    
    ;; Define a record named Thing with a single field a.  Implement
    ;; FileNameMap interface and provide an implementation for the single
    ;; method:  String getContentTypeFor(String fileName)
    (defrecord Thing [a]
      FileNameMap
        (getContentTypeFor [this fileName] (str a "-" fileName)))
    -> user.Thing
    
    ;; construct an instance of the record
    (def thing (Thing. "foo"))
    -> #'user/thing
    
    ;; check that the instance implements the interface
    (instance? FileNameMap thing)
    -> true
    
    ;; get all the interfaces for the record type
    (map #(println %) (.getInterfaces Thing))
    -> java.net.FileNameMap
    -> clojure.lang.IObj
    -> clojure.lang.ILookup
    -> clojure.lang.IKeywordLookup
    -> clojure.lang.IPersistentMap
    -> java.util.Map
    -> java.io.Serializable
    
    ;; actually call the method on the thing instance and pass "bar"
    (.getContentTypeFor thing "bar")
    -> "foo-bar"
  • ;; prepare a protocol
    user=> (defprotocol Fun-Time (drinky-drinky [_]))
    Fun-Time
    
    ;; define a record and extend the previous protocol, implementing its function
    user=> (defrecord Someone [nick-name preffered-drink] Fun-Time (drinky-drinky [_] (str nick-name "(having " preffered-drink "): uuumm")))
    user.Someone
    ;; NOTE how 'nick-name' and 'preffered-drink' are symbols that are not declared anywhere, they are 'provided' inside the function
    
    ;; instantiate the protocol once and store it
    user=> (def dude (->Someone "belun" "daiquiri"))
    #'user/dude
    
    ;; use the function defined inside the protocol on the protocol instance
    user=> (drinky-drinky dude)
    "belun(having daiquiri): uuumm"
    
    
    ;; courtessy of Howard Lewis Ship - http://java.dzone.com/articles/changes-cascade-and-cautionary
  • ; If you define a defrecord in one namespace and want to use it
    ; from another, first require the namespace and then import
    ; the record as a regular class.
    ; The require+import order makes sense if you consider that first
    ; the namespace has to be compiled--which generates a class for
    ; the record--and then the generated class must be imported.
    ; (Thanks to raek in #clojure for the explanations!)
    
    ; Namespace 1 in "my/data.clj", where a defrecord is declared
    (ns my.data)
    
    (defrecord Employee [name surname])
    
    
    ; Namescape 2 in "my/queries.clj", where a defrecord is used
    (ns my.queries
      (:require my.data)
      (:import [my.data Employee]))
    
    (println
      "Employees named Albert:"
      (filter #(= "Albert" (.name %))
        [(Employee. "Albert" "Smith")
         (Employee. "John" "Maynard")
         (Employee. "Albert" "Cheng")]))
      
  • ;; The map->Recordclass form works only in Clojure 1.3 or higher
    
    (defrecord Foo [a b])
    
    (defrecord Bar [a b c])
    
    (defrecord Baz [a c])
    
    (def f (Foo. 10 20))
    (println f)
    -> #user.Foo{:a 10, :b 20}
    
    (def r (map->Bar (merge f {:c 30})))
    (println r)
    -> #user.Bar{:a 10, :b 20, :c 30}
    
    (def z (map->Baz (merge f {:c 30})))
    (println z)
    -> #user.Baz{:a 10, :c 30, :b 20}
Log in to add / edit an example.

See Also top

Log in to add a see also.

Plus_12x12 Minus_12x12 Source clojure/core_deftype.clj:257 top

(defmacro defrecord
  "Alpha - subject to change
  
  (defrecord name [fields*]  options* specs*)
  
  Currently there are no options.

  Each spec consists of a protocol or interface name followed by zero
  or more method bodies:

  protocol-or-interface-or-Object
  (methodName [args*] body)*

  Dynamically generates compiled bytecode for class with the given
  name, in a package with the same name as the current namespace, the
  given fields, and, optionally, methods for protocols and/or
  interfaces.

  The class will have the (immutable) fields named by
  fields, which can have type hints. Protocols/interfaces and methods
  are optional. The only methods that can be supplied are those
  declared in the protocols/interfaces.  Note that method bodies are
  not closures, the local environment includes only the named fields,
  and those fields can be accessed directy.

  Method definitions take the form:

  (methodname [args*] body)

  The argument and return types can be hinted on the arg and
  methodname symbols. If not supplied, they will be inferred, so type
  hints should be reserved for disambiguation.

  Methods should be supplied for all methods of the desired
  protocol(s) and interface(s). You can also define overrides for
  methods of Object. Note that a parameter must be supplied to
  correspond to the target object ('this' in Java parlance). Thus
  methods for interfaces will take one more argument than do the
  interface declarations. Note also that recur calls to the method
  head should *not* pass the target object, it will be supplied
  automatically and can not be substituted.

  In the method bodies, the (unqualified) name can be used to name the
  class (for calls to new, instance? etc).

  The class will have implementations of several (clojure.lang)
  interfaces generated automatically: IObj (metadata support) and
  IPersistentMap, and all of their superinterfaces.

  In addition, defrecord will define type-and-value-based =,
  and will defined Java .hashCode and .equals consistent with the
  contract for java.util.Map.

  When AOT compiling, generates compiled bytecode for a class with the
  given name (a symbol), prepends the current ns as the package, and
  writes the .class file to the *compile-path* directory.

  Two constructors will be defined, one taking the designated fields
  followed by a metadata map (nil for none) and an extension field
  map (nil for none), and one taking only the fields (using nil for
  meta and extension fields)."
  {:added "1.2"}

  [name [& fields] & opts+specs]
  (let [gname name
        [interfaces methods opts] (parse-opts+specs opts+specs)
        ns-part (namespace-munge *ns*)
        classname (symbol (str ns-part "." gname))
        hinted-fields fields
        fields (vec (map #(with-meta % nil) fields))]
    `(let []
       ~(emit-defrecord name gname (vec hinted-fields) (vec interfaces) methods)
       (import ~classname)
       ~(build-positional-factory gname classname fields)
       (defn ~(symbol (str 'map-> gname))
         ([m#] (~(symbol (str classname "/create")) m#)))
       ~classname)))
Vars in clojure.core/defrecord:
Used in 0 other vars

Comments top

No comments for defrecord. Log in to add a comment.