Allgemeiner ausgedrückt geht es bei dieser Frage um verschiedene Ansätze zum Ausdrucksproblem. Die Idee ist, dass Ihr Programm eine Kombination aus einem Datentyp und den Operationen darüber ist. Wir möchten neue Fälle hinzufügen können, ohne die alten Klassen neu kompilieren zu müssen.
Jetzt hat Haskell einige wirklich tolle Lösungen für das Ausdrucksproblem mit dem TypeClass. Insbesondere können wir Folgendes 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
In Clojure gibt es Multimethoden - so können Sie Folgendes 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
Außerdem hat Clojure Protokolle - mit denen Sie Folgendes 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 behauptet dieser Einzelne:
Aber es gibt Protokolle und Multimethoden. Diese sind sehr leistungsstark, aber nicht so leistungsstark wie Haskells Typklassen. Sie können etwas Ähnliches wie eine Typklasse einführen, indem Sie Ihren Vertrag in einem Protokoll spezifizieren. Dieser setzt jedoch nur auf das erste Argument, während Haskell auf der gesamten Signatur inklusive Rückgabewert setzt. Multimethoden sind leistungsstärker als Protokolle, aber nicht so leistungsstark wie Haskells Dispatch.
Meine Frage lautet: Welche Gründe gibt es dafür, dass Protokolle und Multimethoden in Clojure weniger leistungsfähig für Polymorphismus sind als Typklassen in Haskell?