Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

New to clojure, trying to define an effective class with mutable fields via deftype, definitions below.

My program is single threaded, no one else will ever use my code, promised. I know all the advantages of immutability, but this program makes over 500,000,000 updates, so the GC would be the main actor, not my code. I've tried that.

Two problems I've got at run time.

*1. ; Execution error (IllegalArgumentException) at layout.core.Individual/idivlCopy (core.clj:161).
    ; No matching field found: fitness for class layout.core.Individual

I understand that the fields of a deftype are not public, but the code of idivlCopy is in the deftype itself. It seems to me, that the runtime knows (.fitness this) but not (.fitness that).

  • What is the rationale of this behaviour and how can I get around it?
  • How can I access the private protected fields (of that) from the private (i.e. deftype) code area?!

`

*2. ; Execution error (ClassCastException) at layout.core.Individual/idivlSwap (core.clj:173).
           ; class clojure.core.Vec cannot be cast to class clojure.lang.IEditableCollection
           ;    (clojure.core.Vec and clojure.lang.IEditableCollection are in unnamed module of loader 'app')

`

  • The annotations tell that both fields are volatile-mutable. Why is this not known at run time for the chromosome vector? Why can't I (assoc! ) my volatile-mutable chromosome vector-of :char -s?

(definterface IIndividual

  (idivlCopy [that])                      ; Copies that Individual to this
  (idivlSwap [^Integer xP, ^Integer xQ])) ; Swaps two genes in the chromosome
  ; and somme more.
)

(deftype Individual                ; An individual has a fitness value and a chromosome
  [^{:volatile-mutable true}  ^Integer fitness,       ; Integer
   ^{:volatile-mutable true}           chromosome]    ; vector-of :char
 
  IIndividual

	:
	.

  (idivlCopy         ; Copies that Individual to this
    [_, that] 
*1  (set! fitness    (.fitness    that))         ; (set! fitness  999) worked!!
    (set! chromosome (.chromosome that))
    nil)

  (idivlSwap         ; Swaps the genes in the chromosome at alleles P and Q
    [this,
     ^Integer iP,                 ; index of allele P
     ^Integer iQ]                 ; index of allele Q
    (let [gP (chromosome iP),     ; gene  at allele P
          gQ (chromosome iQ)]     ; gene  at allele Q
*2    (assoc! chromosome iP gQ)
      (assoc! chromosome iQ gP)
      nil))

    ; and some more
    :
	.
)

1 Answer

0 votes
by

What you're doing requires a few things that you don't seem to be familiar about.

  • You almost never want ^Integer, most often it's ^long
  • Clojure doesn't know what that is because you haven't tagged it with IIndividual
  • assoc! works only on transitive collections and returns a new collection - you have to use its return value, but in this case you don't need assoc! at all, you need a combination of set! and assoc

Here's a working version:

(definterface IIndividual
  (idivlCopy [that])
  (idivlSwap [^long xP ^long xQ]))

(deftype Individual [^:volatile-mutable ^long fitness
                     ^:volatile-mutable chromosome]
  IIndividual
  (idivlCopy [_this that]
    (let [^Individual that that]
      (set! fitness (.-fitness that))
      (set! chromosome (.-chromosome that))
      nil))

  (idivlSwap [_this ^long iP ^long iQ]
    (let [gP (chromosome iP)
          gQ (chromosome iQ)]
      (set! chromosome (assoc chromosome iP gQ iQ gP))
      nil)))

(let [i1 (Individual. 1 [:TP53 :TNF])
      i2 (Individual. 2 [:EGFR :VEGFA])]
  (.idivlCopy i1 i2)
  (.idivlSwap i1 0 1))

However, note that the swap method still uses an immutable vector, which might not be what you want. If you decide to use a mutable array of longs, you'd have to also change the copy method so that the array is not just assigned but copied as well to avoid sharing the same data between instances.

Also note that if your data is in any way representable as columns you would absolutely be better off using things like Tablecloth, in every single regard. BTW, Clojure has a very friendly data science community on Zulip where the authors of Tablecloth hang out.

...