Mir scheint, dass Sie immer Funktionsargumente übergeben können, anstatt eine Typklasse zu verwenden. Zum Beispiel, anstatt eine Gleichheitstypklasse zu definieren:
class Eq a where
(==) :: a -> a -> Bool
Die Verwendung in anderen Funktionen zur Angabe des Typarguments muss eine Instanz von Eq
:
elem :: (Eq a) => a -> [a] -> Bool
Können wir unsere elem
Funktion nicht einfach ohne Verwendung einer Typklasse definieren und stattdessen ein Funktionsargument übergeben, das den Job erledigt?
Monad m
Einschränkung sagt mir mehr als das Übergeben zusätzlicher Funktionsargumente vom Typa -> m a
undm a -> (a -> m b) -> m b
.TypeApplications
Erweiterung können Sie das implizite Argument explizit machen.(==) @Int 3 5
vergleicht3
und5
speziell alsInt
Werte. Sie können sich@Int
einen Schlüssel im Wörterbuch typspezifischer GleichheitsfunktionenInt
vorstellen und nicht die spezifische Vergleichsfunktion selbst.Antworten:
Ja. Dies wird als "Wörterbuchübergabestil" bezeichnet. Manchmal, wenn ich einige besonders knifflige Dinge mache, muss ich eine Typklasse verschrotten und in ein Wörterbuch verwandeln, da die Wörterbuchübergabe leistungsfähiger ist 1 , aber oft recht umständlich, wodurch konzeptionell einfacher Code ziemlich kompliziert aussieht. Ich verwende manchmal den Wörterbuch-Übergabestil in Sprachen, die nicht Haskell sind, um Typklassen zu simulieren (habe aber gelernt, dass dies normalerweise keine so gute Idee ist, wie es sich anhört).
Wenn es einen Unterschied in der Ausdruckskraft gibt, gibt es natürlich einen Kompromiss. Während Sie eine bestimmte API auf mehr Arten verwenden können, wenn sie mit DPS geschrieben wurde, erhält die API weitere Informationen, wenn Sie dies nicht können. Dies zeigt sich in der Praxis unter anderem
Data.Set
darin, dass es nur einOrd
Wörterbuch pro Typ gibt. DasSet
speichert seine Elemente sortiert nach.Ord
Wenn Sie einen Satz mit einem Wörterbuch erstellen und dann ein Element mit einem anderen einfügen, wie dies mit DPS möglich wäre, können Sie dieSet
Invariante brechen und zum Absturz bringen. Dieses Eindeutigkeitsproblem kann mithilfe eines Phantom-Existenzials gemindert werdenGeben Sie ein, um das Wörterbuch zu markieren, aber auf Kosten einer ziemlich nervigen Komplexität in der API. Dies wird auch in derTypeable
API ziemlich ähnlich angezeigt.Das Einzigartigkeitsbit kommt nicht sehr oft vor. Was Typklassen großartig können, ist das Schreiben von Code für Sie. Zum Beispiel,
Das erfordert zwei "Prozessoren", die eine Eingabe annehmen und möglicherweise eine Ausgabe geben und diese verketten, wobei sie abgeflacht werden
Nothing
, müsste in DPS wie folgt geschrieben werden:Wir mussten im Wesentlichen den Typ buchstabieren, bei dem wir ihn erneut verwenden, obwohl wir ihn bereits in der Typensignatur geschrieben haben, und selbst das war überflüssig, da der Compiler bereits alle Typen kennt. Da es nur einen Weg gibt, einen bestimmten
Semigroup
Typ zu erstellen, kann der Compiler dies für Sie tun. Dies hat einen Effekt vom Typ "Zinseszins", wenn Sie anfangen, viele parametrische Instanzen zu definieren und die Struktur Ihrer Typen zu verwenden, um wie in denData.Functor.*
Kombinatoren für Sie zu berechnen , und dies wird mit großer Wirkung verwendet,deriving via
wenn Sie im Wesentlichen alle erhalten können "Standard" algebraische Struktur Ihres Typs für Sie geschrieben.Und lassen Sie mich nicht einmal mit MPTCs und Fundeps anfangen, die Informationen in Typchecking und Inferenz zurückführen. Ich habe noch nie versucht, so etwas in DPS umzuwandeln - ich vermute, es würde bedeuten, viele Beweise für die Gleichstellung von Typen weiterzugeben -, aber ich bin mir auf jeden Fall sicher, dass es viel mehr Arbeit für mein Gehirn bedeuten würde, als ich mir bequem machen würde mit.
- -
1 U Nless Sie verwenden ,
reflection
in welchem Fall sie in Kraft äquivalent werden - aberreflection
auch umständlich zu bedienen sein.quelle
Ja. Das (Dictionary Passing genannt) ist im Grunde das, was der Compiler sowieso mit Typklassen macht. Für diese Funktion würde es buchstäblich so aussehen:
Anrufen
elemBy (==) x xs
ist jetzt gleichbedeutend mitelem x xs
. Und in diesem speziellen Fall können Sie noch einen Schritt weiter gehen: Sieeq
haben jedes Mal das gleiche erste Argument, sodass Sie es in die Verantwortung des Anrufers legen können, dies anzuwenden, und am Ende Folgendes:Anrufen
elemBy2 (x ==) xs
ist jetzt gleichbedeutend mitelem x xs
....Oh, Moment mal. Das ist nur so
any
. (Und tatsächlich in der Standardbibliothekelem = any . (==)
.)quelle
implicit
und der Compiler würde sie aus dem Gültigkeitsbereich für Sie einfügen.