; A protocol is a set of methods with no implementations
(defprotocol Fly
  "A simple protocol for flying"
  (fly [this] "Method to fly"))


; Protocol implementations are checked first for direct definitions (defrecord, deftype, reify),
; then metadata definitions,
; then external extensions (extend, extend-type, extend-protocol)


; impl with defrecord
(defrecord Bird [name]
  Fly
  (fly [this] (str (:name this) " flies...")))

(def crow (->Bird "Crow"))

(fly crow)
;=> "Crow flies..."

; record can be extended later with extend-type

; record vs. map
; The only difference between a record+protocols and a map+functions is polymorphism.
; If you’re thinking "I need a record and protocols, but I don’t need polymorphism" then the solution is probably a map plus functions.


; deftype: class implement protocol without map-like features, e.g. (:name this) will not work
(deftype Aircraft [name]
  Fly
  (fly [this] (str (.name this) " flies..")))

(def hurricane (->Aircraft "Hurricane"))

(fly hurricane)
;=> "Hurricane flies.."


; reify: anonymous class implement protocol ~> can’t define fields
(fly (reify Fly
       (fly [_] "fly by reify")))

; reify vs. proxy
; reify is Clojure semantics and proxy is Java/host semantics


; extend exiting String type
(extend-protocol Fly
  String
  (fly [this] (str this " flies..")))

(fly "butterfly")
;=> "butterfly flies.."