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?
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.
Clojure has gen-class, reify, proxy and also deftype and defrecord to define new class-like datatypes.
Clojure let is used to define new variables in a local scope. These local variables give names to values. In Clojure, they cannot be re-assigned, so we call them immutable.
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, e.g.
(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]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With