2014-02-26 3 views
20

Allgemeiner bezieht sich diese Frage auf verschiedene Ansätze zur expression problem. Die Idee ist, dass Ihr Programm eine Kombination aus einem Datentyp und Operationen darüber ist. Wir möchten in der Lage sein, neue Fälle hinzuzufügen, ohne die alten Klassen neu zu kompilieren.Aus welchen Gründen sind Protokolle und Multimethoden in Clojure weniger leistungsfähig für Polymorphie als Typklassen in Haskell?

Jetzt hat Haskell einige wirklich tolle Lösungen für die expression problem mit der TypeClass. Insbesondere - können wir tun:

class Eq a where 
    (==) :: a -> a -> Bool 
    (/=) :: a -> a -> Bool 


member :: (Eq a) => a -> [a] -> Bool 
member y [] = False 
member y (x:xs) = (x == y) || member y xs 

Jetzt in Clojure dort multimethods sind - so können Sie tun:

(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 

Auch in Clojure Sie haben protocols - mit denen Sie tun können:

(defprotocol P 
    (foo [x]) 
    (bar-me [x] [x y])) 

(deftype Foo [a b c] 
    P 
    (foo [x] a) 
    (bar-me [x] b) 
    (bar-me [x y] (+ c y))) 

(bar-me (Foo. 1 2 3) 42) 
=> 45 

(foo 
(let [x 42] 
    (reify P 
    (foo [this] 17) 
    (bar-me [this] x) 
    (bar-me [this y] x)))) 

=> 17 
Jetzt

this individual makes the claim:

Aber es gibt Protokolle und Multi-Methoden. Diese sind sehr mächtig, aber nicht so mächtig wie Haskells Typklassen. Sie können etwas wie eine Typklasse einführen, indem Sie Ihren Vertrag in einem Protokoll angeben. Dies geschieht nur beim ersten Argument, während Haskell die gesamte Signatur einschließlich des Rückgabewertes versenden kann. Multi-Methoden sind leistungsfähiger als Protokolle, aber nicht so leistungsfähig wie Haskells Versand.

Meine Frage ist: Was die Gründe sind, die Protokolle und Multimethoden in Clojure sind weniger leistungsfähig für Polymorphismus als typeclasses in Haskell?

Antwort

21

Nun, die offensichtliche ist, dass Protokolle nur auf dem ersten und nur dem ersten Argument versenden können. Das heißt, sie sind etwa gleichwertig zu

class Foo a where 
    bar :: a -> ... 
    quux :: a -> ... 
    ... 

Wo amuss das erste Argument sein. Haskells Typklassen lassen a irgendwo in der Funktion erscheinen. Protokolle sind daher weniger ausdrucksstark als Typklassen.

Weiter ist Multimethoden. Multimethoden, wenn ich mich nicht täusche, erlauben den Versand basierend auf einer Funktion aller Argumente. Dies wirkt in mancher Hinsicht aussagekräftiger als Haskell, da Sie Argumente des gleichen Typs unterschiedlich versenden können. Dies kann jedoch tatsächlich in Haskell erfolgen, indem das Argument im Allgemeinen in einen neuen Typ für das Dispatching eingefügt wird.

Ein paar Dinge, die nicht mit Multimethoden meines Wissens getan werden kann:

  1. Versand auf Rückgabetyp
  2. Werte polymorphen
  3. Shop über alle Arten von einer Typklasse forall a. Foo a => a

Um zu sehen, wie 1. ins Spiel kommt, betrachten Sie Monoid es hat einen Wert mempty :: Monoid m => m. Es ist keine Funktion und das Simulieren ist unmöglich mit Clojure, da wir keine Informationen darüber haben, welche Methode wir wählen sollen.

Für 2 betrachten read :: Read a => String -> a.In Haskell konnten wir tatsächlich eine Liste mit dem Typ [forall a. Read a => a] erstellen, wir haben die Berechnung im Wesentlichen aufgeschoben und können jetzt Elemente der Liste ausführen und erneut ausführen, um sie als unterschiedliche Werte zu lesen.

Typklassen haben auch statische Typen, also gibt es einige Überprüfungen, um sicherzustellen, dass Sie nicht "stecken bleiben", ohne eine Instanz statisch zu benennen, aber Clojure wird dynamisch getippt Stil zwischen den beiden Sprachen statt einen bestimmten Vorteil auf die eine oder andere Weise. Der Vorteil ist natürlich, dass Typklassen viel weniger Overhead haben als Multimethoden, da im Allgemeinen der Zeugendatensatz inline sein kann und alles statisch aufgelöst wird.

+4

Problem: "Ich möchte nicht unnötig einschränken, welche Art von Daten mein Code verarbeitet." ... "Ich weiß, ich werde dynamisches Tippen verwenden." Jetzt hast du acht Probleme. ;) – AndrewC

+8

@AndrewC Anstiftung zur religiösen Debatte über das Tippen ist nicht notwendig. Es gibt Vorteile für beide, egal auf welcher Seite des Arguments Sie stehen. –

+0

@ A.Webb OK vielleicht wäre es fairer zu sagen, dass statische Typisierung mit parametrischen Polymorphie und Typ Klassen wählt, um dieses Problem zur Kompilierzeit zu lösen, während dynamische Typisierung wählt, um dieses Problem zur Laufzeit zu lösen. – AndrewC

7

Der grundlegendste Unterschied ist, dass mit Typklassen, Versand auf Typen ist nicht auf Werte. Für die Ausführung ist kein Wert erforderlich. Das erlaubt viel allgemeinere Fälle. Das offensichtlichste Beispiel ist die Verteilung auf einen Ergebnistyp einer Funktion. Betrachten wir z.B. Haskell's Read Klasse:

Ein solcher Versand ist bei Multi-Methoden, die auf ihre Argumente absenden müssen, eindeutig unmöglich.

Siehe auch meine umfangreichere comparison with plain OO.

Verwandte Themen