Clojure: aggiunta di funzioni a defrecord senza definire un protocollo

3

Un record può implementare i protocolli. Tuttavia, ogni volta che penso a una nuova funzione che vorrei implementare dal record, devo aggiungerla a un protocollo esistente o creare un nuovo protocollo. Come faccio a evitare questo? Idealmente, vorrei solo associare una funzione a un record senza dover definire un protocollo.

    
posta Zachary K 17.02.2011 - 02:15
fonte

2 risposte

2

Quindi scrivi una funzione che prende un'istanza di quel record come argomento.

defrecord non "implementa" un protocollo, nel senso che la classe in un linguaggio OO simile a Java implementa un'interfaccia. Quello che fa è definire un tipo di dati e darvi dello zucchero sintattico per implementare un protocollo che opera su quel tipo di dati. Esiste una panoramica molto buona su quale tipo di protocolli di problemi si intende risolvere qui: link

    
risposta data 17.02.2011 - 02:31
fonte
2

Ottima domanda. Ecco una risposta basata su uno simile che ho dato su StackOverflow.

Come al solito, c'è un bel modo di fare cose in Clojure - ecco come implementare il tuo semplice sistema OO dinamico (includendo ereditarietà, polimorfismo e incapsulamento) in 10 linee di Clojure.

L'idea: puoi mettere le funzioni nelle normali mappe o record Clojure se vuoi, creando una struttura simile a OO. Puoi quindi usarlo in uno stile "prototipo".

; define a prototype instance to serve as your "class"
; use this to define your methods, plus any default values
(def person-class
  {:get-full-name 
    (fn [this] (str (:first-name this) " " (:last-name this)))})

; define an instance by merging member variables into the class
(def john 
  (merge person-class 
    {:first-name "John" :last-name "Smith"}))

; macro for calling a method - don't really need it but makes code cleaner
(defmacro call [this method & xs]
  '(let [this# ~this] ((~method this#) this# ~@xs)))

; call the "method"
(call john :get-full-name)
=> "John Smith"

; added bonus - inheritance for free!
(def mary (merge john {:first-name "Mary"}))
(call mary :get-full-name)
=> "Mary Smith"
    
risposta data 17.02.2011 - 12:17
fonte

Leggi altre domande sui tag