다형성(Polymorphism)에 대한 이해 (Clojure 언어)

20 Apr 2020 - Jake Na

Basic References

Polymorphism 이란?

[fn:1] https://clojure.org

Sequences

Multimethods

Protocols

[fn:2] clojure for the brave and true 책에서 발취됨

(ns data-psychology)
(defprotocol Psychodynamics
  "Plumb the inner depths of your data types"
  (thoughts [x] "The data type's innermost thoughts")
  (feeling-about [x] [x y] "Feeling about self or other"))

;; implement the protocol in a specific type
(extend-type java.lang.String
  Psychodynamics
  (thoughts [x] (str x " thinks, 'Truly, the character defines the data type'"))
  (feeling-about
    ([x] (str x " is longing for a simpler way of life"))
    ([x y] (str x " is envious of " y "'s simpler way of life"))))

(thoughts "blorb")
; => "blorb thinks, 'Truly, the character defines the data type'"

(feeling-about "schmorb" 2)
; => "schmorb is envious of 2's simpler way of life

;; if you want default behaviour of this protocol for any type, then
(extend-type java.lang.Object
  Psychodynamics
  (thoughts [x] "Maybe the Internet is just a vector for toxoplasmosis")
  (feelings-about
    ([x] "meh")
    ([x y] (str "meh about " y))))

(thoughts 3)
; => "Maybe the Internet is just a vector for toxoplasmosis"

(feelings-about 3)
; => "meh"

(feelings-about 3 "blorb")
; => "meh about blorb"

;; or you can use extend-protocol to define the same protocol for multiple types
(extend-protocol Psychodynamics
  java.lang.String
  (thoughts [x] "Truly, the character defines the data type")
  (feelings-about
    ([x] "longing for a simpler way of life")
    ([x y] (str "envious of " y "'s simpler way of life")))

  java.lang.Object
  (thoughts [x] "Maybe the Internet is just a vector for toxoplasmosis")
  (feelings-about
    ([x] "meh")
    ([x y] (str "meh about " y))))

Records

(ns were-records)
(defrecord WereWolf [name title])

;; you can create an instance of this record in three ways

(WereWolf. "David" "London Tourist")
; => #were_records.WereWolf{:name "David", :title "London Tourist"}

(->WereWolf "Jacob" "Lead Shirt Discarder")
; => #were_records.WereWolf{:name "Jacob", :title "Lead Shirt Discarder"}

(map->WereWolf {:name "Lucian" :title "CEO of Melodrama"})
; => #were_records.WereWolf{:name "Lucian", :title "CEO of Melodrama"}

;; you can import the records in another namespece, you'll have to import it.
(ns monster-mash
  (:import [were_records WereWolf])) ; Notice that were_records has an underscore, not a dash
(WereWolf. "David" "London Tourist")
; => #were_records.WereWolf{:name "David", :title "London Tourist"}


(def jacob (->WereWolf "Jacob" "Lead Shirt Discarder"))
(.name jacob)
; => "Jacob"
(:name jacob)
; => "Jacob"
(get jacob :name)
; => "Jacob"

;; When testing for equality, Clojure will check that all fields are equal and that the two comparands have the same type:
(= jacob (->WereWolf "Jacob" "Lead Shirt Discarder"))
; => true
(= jacob (WereWolf. "David" "London Tourist"))
; => false
(= jacob {:name "Jacob" :title "Lead Shirt Discarder"})
; => false !!! a record is not a map, i.e. it's a different type !!!

;; Any function you can use on a map, you can also use on a record
(assoc jacob :title "Lead Third Wheel")
; => #were_records.WereWolf{:name "Jacob", :title "Lead Third Wheel"}
(dissoc jacob :title)
; => {:name "Jacob"} <- that's not a were_records.WereWolf
; REASON is that if you dissoc a record and then try to call a protocol method on the result, the record's protocol method won't be called.

;; you would extend a protocol when defining a record:
(defprotocol WereCreature
  (full-moon-behavior [x]))

(defrecord WereWolf [name title]
  WereCreature
  (full-moon-behavior [x]
    (str name " will howl and murder")))

(full-moon-behavior (map->WereWolf {:name "Lucian" :title "CEO of Melodrama"}))
; => "Lucian will howl and murder"

Further data types[fn:3] for abstraction

deftype

reify

proxy

[fn:3] http://clojure.org/datatypes/