I agree that deftype
(or possibly defrecord
) is a better than proxy
to do this in Clojure, but see my comments at the end to consider all possibilities.
For your question after UPDATE 2.
You can add "properties" to records by specifying them in the arglist:
(deftype Particle [position radius prop3 prop4]
...
)
Remember that types in Clojure are immutable, so there is no concept of setting properties after creating the entity. If some of the properties are optional, it is recommended best practice to create helper "factory" methods, like:
(defn make-particle
([position] (Particle. position nil nil nil))
([position radius] (Particle. position radius nil nil))
;; etc. add more here as needed
)
An option to consider is to drop types entirely and just use maps, which have within them whatever "properties/fields" you need. Types are useful when you need to implement abstractions. For your ParticleProtocol - what is the value it is providing? Protocols are meant to provide a way to have polymorphism, so will you have multiple implementations of this protocol?
Chas Emerick did an in depth flowchart of how to choose a data type in Clojure that may help you: http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/
[Update showing example map implementation]:
To construct a map with a "property" and retrieve that property you would do:
(def mymap {:myfloat 3.1415926})
(println "myfloat has value:" (:myfloat mymap))
To provide additional functionality, such as a "render" function, just create a fn that accepts a map with the desired keys:
;; the details are bogus, just showing the syntax
(defn render [m]
(no-stroke)
(fill (:radius m) (:position m))
(do-something-else (:position m)))
For your update
, if you meant to update the values in the particle map, then you need to create a new map, rather than updating an existing one.
(def myparticle {:position 100 :radius 25})
(defn change-pos [particle-map new-pos]
(assoc-in particle-map [:position] new-pos))
(let [new-particle (change-pos myparticle 300)]
(println new-particle))
;; prints out {:position 300 :radius 25}
;; orig myparticle still has value {:position 100 :radius 25}
;; or do it directly
(println (assoc myparticle :position 300))
;; prints out {:position 300 :radius 25}