简体   繁体   中英

Clojure's defrecord - how to use it?

I'm attempting to create my own immutable datatype/methods with defrecord in Clojure. The goal is to have a datatype that I can create instances of, and then call its methods to return a new copy of itself with mutated variables. Say a and b are vectors. I'd like to update a value in both and return a new copy of the entire structure with those vectors updated. This obviously doesn't compile, I'm just trying to get my ideas across.

(defrecord MyType [a b]
  (constructor [N]
    ; I'd like to build an initial instance, creating a and b as vectors of length N
  ) 

  (mutate-and-return [] 
    ; I'd like to mutate (assoc the vectors) and return the new structure, a and b modified
  )
)

I'd like to call the constructor and then the mutator as many times as I'd like (there are other functions that don't mutate, but I don't want to make it more complex for the question).

Alternatively, if this is not idiomatic Clojure, how are you supposed to do something like this?

Here's how you define your record:

(defrecord MyType [a b])

Note that in Clojure you don't typically define "methods" within your record type itself (the exception is if you want to directly implement a Java interface or a protocol).

A basic constructor (prefixed with -> ) gets generated automatically for free:

(def foo (->MyType [1 2 3] [4 5 6]))

foo
=> #user.MyType{:a [1 2 3], :b [4 5 6]}

You can then write more sophisticated constructor functions that use this, eg

(defn mytype-with-length [n]
  (let [a (vec (range n))
        b (vec (range n))] 
    (->MyType a b)))

(mytype-with-length 3)
=> #user.MyType{:a [0 1 2], :b [0 1 2]}

And "mutate-and-return" also comes for free - you can just use assoc :

(assoc foo :b [7 8 9])
=> user.MyType{:a [1 2 3], :b [7 8 9]}

Clojure defrecord example:

;;define Address record

(defrecord Address [city state])

;;define Person record

(defrecord Person [firstname lastname ^Address address])

;;buid the constructor

(defn make-person ([fname lname city state]
               (->Person fname lname (->Address city state))))

;;create a person

(def person1 (make-person "John" "Doe" "LA" "CA"))

;;retrieve values

(:firstname person1)
(:city (:address person1))

Clojure allows you to create records, which are custom, maplike data types. They're maplike in that they associate keys with values, you can look up their values the same way you can with maps, and they're immutable like maps.


(defrecord Person [last first address])
;=> core.Person

(defrecord Ad [street city zip])
;=> core.Ad

(def p1 (Person. "Jhon" "Mick"
                 (Ad. "US187956" "NY" 3369)))
;=> #'core/p1

(update-in p1 [:address :zip] inc)
;=> #core.Person{:last "Jhon", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}

(assoc p1 :last "Adam")
;=> #core.Person{:last "Adam", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM