Ugrás a tartalomhoz

Szerkesztő:Marcell1202/próbalap

A Wikipédiából, a szabad enciklopédiából

Clojure Multimetódus és Hierarchia[szerkesztés]

A Clojure megkerüli a hagyományos objektum orientált megközelítést, azaz hogy minden új szituációhoz új adattípust hozzon létre, helyette felépít egy nagy függvénykönyvtárat egy kis típusra. A Clojure azonban teljes mértékben elismeri a futás időben történő polimorfizmust, ezáltal flexibilis és bővíthető rendszer architektúrát tesz lehetővé. Támogatja a kifinomult futás idejű polimorfizmust egy multimethod rendszeren keresztül, ami támogatja az egy vagy több argumentumok típusainak, értékeinek, attribútumainak, metaadatainak, és kapcsolatainak továbbküldését (dispatch).


A Clojure multimethod kombinációja a dispatching funkciónak, és egy vagy több metódusnak. Amikor a multimetódust definiálják a defmulti kulcsszóval, akkor a dispatching funkciót kötelező megadni. Ez a funkció lesz használva a multimetódus  argumentumain, hogy előállítsa a dispatching értéket. A multimetódus ezután megpróbálja megkeresni a dispatching értékhez tartozó metódust, vagy azt az értéket ahonnan a dispatching érték származott. Ha a metódus definiálva lett (defmethod kulcsszóval), akkor meg lesz hívva az argumentumokkal, és az lesz az értéke a multimetódus hívásnak. Ha nincs olyan metódus ami a dispatching értékhez tartozik, akkor a multimetódus az alap dispatching értékhez tartozó metódust keres (:default), és azt használja, ha létezik. Különben a hívás hibát dob.


A multimethod rendszer a következő API-t valósítja meg: defmulti egy új multimetódust készít, defmethod egy új metódust készít és telepít, ami összefüggésben van a dispatch értékkel, remove-method eltávolítja a dispatch értékkel összefüggő metódust, a prefer-method pedig rendezést készít a metódusok között amikor kétértelműek lehetnek.


A származtatás meghatározott Java öröklődés (osztály értékekre), vagy a Clojure ad-hoc hierarchia rendszer kombinációja alapján. A hierarchia rendszer támogatja a származtatását kapcsolatoknak nevek között (szimbólumok vagy kulcsszavak), és kapcsolatoknak osztályok és nevek között. A derive funkció készíti el ezeket a kapcsolatokat, és az isa? funkciót tesztelni a létezésüket. (Az isa? nem instance?)


Megadhatóak hierarchikus kapcsolatok a következővel: (derice child parent). A gyerek (child) és szülő (parent) lehet egyaránt szimbólum vagy kulcsszó, és kötelezően névtér meghatározott (namespace-qualified):

Megjegyzés: a ::reader syntax, ::keywords névtereket valósítanak meg.

::rect
    -> :user/rect

derive az alapvető kapcsolatépítő

(derive ::rect ::shape)
(derive ::square ::rect)

parents (szülők) / ancestors (ősök) / descendants (leszármazottak) és isa? a hierarchia lekérdezésére szolgál

(parents ::rect)
-> #{:user/shape}

(ancestors ::square)
-> #{:user/rect :user/shape}

(descendants ::shape)
-> #{:user/rect :user/square}

(= x y) implementálja (isa? x y)

(isa? 42 42)
-> true

isa? a hierarchia rendszert használja

(isa? ::square ::shape)
-> true

Használható egy osztály gyerekként is (de nem a szülő, az egyetlen lehetőség hogy valamit gyerekké tegyünk Java öröklődéssel lehetséges) Ez lehetővé teszi új rendszerek ráhelyezését már létező Java osztályhierarchiára.

(derive java.util.Map ::collection)
(derive java.util.Collection ::collection)

(isa? java.util.HashMap ::collection)
-> true

isa? teszteli az osztály kapcsolatait

(isa? String Object)
-> true

szülők (parents) / ősök (ancestors) is szintén (de leszármazottak (descendants) nem, mivel ezek nyílt halmazok)

(ancestors java.util.ArrayList)
-> #{java.lang.Cloneable java.lang.Object java.util.List
    java.util.Collection java.io.Serializable
    java.util.AbstractCollection
    java.util.RandomAccess java.util.AbstractList}

isa? vektorokkal dolgozik, úgy hogy meghívja az isa?-t a megfelelő elemekre:

(isa? [::square ::rect] [::shape ::shape])
-> true

isa? alapú küldés (dispatch)[szerkesztés]

A multimetódusok isa?-t használnak = helyett amikor tesztelik a dispatch értékek egyezését. Fontos, hogy az isa? első tesztje =, tehát a pontos egyezések működnek.

(defmulti foo class)
(defmethod foo ::collection [c] :a-collection)
(defmethod foo String [s] :a-string)

(foo [])
:a-collection

(foo (java.util.HashMap.))
:a-collection

(foo "bar")
:a-string

prefer-method egyértelműsítésre szolgál, abban az esetben ha több egyezés van ahol egyik sem dominánsabb mint a másik. Egyszerűen deklarálható multimetódusonként, hogy melyik dispatch érték részesüljön előnyben.

(derive ::rect ::shape)

(defmulti bar (fn [x y] [x y]))
(defmethod bar [::rect ::shape] [x y] :rect-shape)
(defmethod bar [::shape ::rect] [x y] :shape-rect)

(bar ::rect ::rect)
-> Execution error (IllegalArgumentException) at user/eval152 (REPL:1).
   Multiple methods in multimethod 'bar' match dispatch value:
   [:user/rect :user/rect] -> [:user/shape :user/rect]
   and [:user/rect :user/shape], and neither is preferred

(prefer-method bar [::rect ::shape] [::shape ::rect])
(bar ::rect ::rect)
-> :rect-shape

A fenti példák mindegyike a globális hierarchiát használja, ami a multimetódus rendszer által van használva, de teljes különálló hierarchiák is kiépíthetőek make-hierarchy-val, és a fenti funkciók felvehetnek opcionális hierarchiát első argumentumként.


Ez a rendszer extrém hatékony. Egy út hogy megértsük a kapcsolatokat a Clojure multimetódusok és a tradicionális Java stílusú egyszeres dispatch között, hogy az egyszeres dispatch olyan mint a Clojure multimethod, aminek a dispatch funkciója meghívja a getClass-t az első argumentumon, amelynek a metódusai kapcsolatban állnak azokkal az osztályokkal. A Clojure multimetódusok nem erősen kötöttek osztály/típusra nézve, bármilyen  argumentum tulajdonságon alapulhatnak, több argumentumon, csinálhatnak validációt rajtuk, és eljuthatnak hibakezelő metódusokig.

Megjegyzés: Ebben a példában a :Shape kulcsszó dispatch funkcióként van használva, mivel a kulcsszavak a map-ek funkciói, ahogy az az adatstruktúrák szekcióban áll.

(defmulti area :Shape)
(defn rect [wd ht] {:Shape :Rect :wd wd :ht ht})
(defn circle [radius] {:Shape :Circle :radius radius})
(defmethod area :Rect [r]
    (* (:wd r) (:ht r)))
(defmethod area :Circle [c]
    (* (. Math PI) (* (:radius c) (:radius c))))
(defmethod area :default [x] :oops)
(def r (rect 4 13))
(def c (circle 12))
(area r)
-> 52
(area c)
-> 452.3893421169302
(area {})
-> :oops

Referenciák[szerkesztés]

https://clojure.org/reference/multimethods