4

I want to create an object with properties and methods in Clojure, I read that gen-class and proxy can do the job I need but its implementation is very confusing for me.

I want to use proxy to avoid AOT compilation steps, I read about it and I though I better learn how to use the easier of the two

Here is what I want to do in Clojure

Java code:

public class MyClass {
    public float myFloat;

    MyClass( float _myFloat ) {
        myFloat = _myFloat
    }

    public void showNumber() {
        println( myFloat );
    }
}

I'm struggling to translate that code to Clojure using proxys, any help will be much appreciated


UPDATE:

Apparently deftype is more suitable for my purposes, but I'm still struggling with its implementation

Here is my Clojure code:

(deftype Particle [x y]
  Object
  (render [this]
    (no-stroke)
    (fill 200 30 180)
    (ellipse x y 200 200)))

Thing is I need to specify a protocol which I'm not sure which one to use, so I'm using Object as I'm trying to create a java-class like object but I get the folloiwng error message:

Can't define method not in interfaces: render

I'm using quill which is a Processing port for Clojure if that helps


UPDATE 2:

OK I manage to get a working defprotocol and deftype combo, but there is 1 more thing I need to leran how to do and that is to add member variables or properties to my class, here is my clojure code:

(defprotocol ParticleProtocol
  (update [this])
  (render [this]))

(deftype Particle [position]
  ParticleProtocol
  (update [this])
  (render [this]
    (no-stroke)
    (fill 200 30 180)
    (ellipse (.x position) (.y position) 20 20)))

To this object I would like to add a couple of variables like radius among others, any ideas?

1
  • 1
    Offtopic: if you're going to develop in Clojure, you have to stop thinking the object oriented way. Commented Mar 17, 2013 at 21:32

2 Answers 2

3

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}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot, I'm new to the language so most of this things are new to me, I'm learning as I do. May I ask any chance to provide an example of my implementation using maps?
OK. I've added a simple set of examples. Let me know if that didn't tell you want you are looking for.
1

You can add the "variables" next to position, like this:

(deftype Particle [position radius]
   ...
  )

position and radius aren't really variables, they are more like final attributes. If you need to "vary" them, you should store atoms in them, like this:

(Particle. (atom (Position. 3 4)) (atom 5.0))

But you should really heed the advise of @m0skit0 to stop thinking in terms of objects and classes and start thinking in functions and immutable data structures.

1 Comment

Thank, Ok stop thinking OO, so immutable data structures I will do my homework

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.