Frage

Typklassen scheint eine große Möglichkeit, zu sein generische und wiederverwendbare Funktionen in einem sehr konsistent zu schreiben , effiziente und erweiterbare Weise. Aber noch nicht "Mainstream-Sprache" bietet sie - im Gegenteil: Konzepte , die eine ganz analogical Idee ist, hat ausgeschlossen von dem nächsten C ++!

Was ist die Argumentation gegen typeclasses? Offenbar viele Sprachen suchen nach einer Möglichkeit, mit ähnlichen Problemen zu kämpfen: .NET generische Einschränkungen und Schnittstellen wie IComparable eingeführt, die Funktionen wie

erlauben
T Max<T>(T a, T b) where T : IComparable<T> { // }

auf allen Arten zu betreiben, die die Schnittstelle implementieren.

Scala nutzt stattdessen eine Kombination aus Merkmale und so genannte implizite Parameter / Ansicht Grenzen , die generischen Funktionen automatisch weitergeleitet werden.

Aber beide Konzepte hier haben gezeigt, große Nachteile -. Schnittstellen sind Vererbung basiert und somit relativ langsam aufgrund indirection und darüber hinaus gibt es keine Möglichkeit zu lassen, eine bestehende Art sie umsetzen

Wenn wir eine Abstraktion für einen Monoid benötigt, könnten wir ziemlich gut eine Schnittstelle schreiben und lassen Sie unsere Typen dies implementieren, aber builtin Typen wie int konnte nie nativ auf Ihre Funktionen arbeiten.

Implizite Parameter statt inkonsistent sind mit regelmäßigen Schnittstellen / Züge.

Mit Typklassen, gäbe es kein Problem (Pseudo-Code) sein

typeclass Monoid of A where
    static operator (+) (x : A, y : A) : A
    static val Zero : A 
end

instance Int of Monoid where
   static operator (+) (x : Int, y : Int) : Int = x + y
   static val Zero : Int = 0
end

Also, warum wir Klassen verwenden Typ nicht? Haben sie gravierende Nachteile, nachdem alle?

Bearbeiten : Bitte verwechseln Sie nicht typeclasses mit struktureller Typisierung, reinen C ++ Vorlagen oder Ente eingeben. A typeclass ist explizit instanziiert von Arten und nicht nur zufrieden vereinbarungs . Darüber hinaus kann es sinnvoll Implementierungen tragen und nicht nur eine Schnittstelle definieren.

War es hilfreich?

Lösung

Konzepte wurden ausgeschlossen, weil der Ausschuss nicht glaube, es ihnen gerade rechtzeitig bekommen konnte, und weil sie die Freisetzung nicht als wesentlich angesehen. Es ist nicht, dass sie nicht denken, sie eine gute Idee sind, sie denken nur nicht der Ausdruck von ihnen für C ++ ist ausgereift: http://herbsutter.wordpress.com/2009/07/21/trip-report/

Statische Typen versuchen Sie zu verhindern, dass ein Objekt an eine Funktion, die nicht die Anforderungen der Funktion nicht erfüllt. In C ++ ist dies eine große große Sache, weil zu der Zeit wird das Objekt durch den Code zugegriffen wird, nicht da ist die Überprüfung, dass es das Richtige ist.

Konzepte versuchen Sie zu verhindern, dass ein Template-Parameter übergeben, die nicht die Anforderungen der Vorlage nicht erfüllt. Aber zu der Zeit der Template-Parameter durch den Compiler zugegriffen wird, bereits ist Überprüfung, dass es das Richtige ist, auch ohne Konzepte. Wenn Sie versuchen, es in einer Art und Weise zu verwenden, nicht unterstützt wird, können Sie einen Compiler-Fehler [*]. Im Fall von schweren schablonen unter Verwendung von Code können Sie drei Bildschirme voller spitzen Klammern, aber im Prinzip, das ist eine informative Nachricht. Die Notwendigkeit, Fehler vor einem gescheiterten Kompilierung zu fangen ist weniger dringlich als die Notwendigkeit, Fehler vor undefiniertem Verhalten zur Laufzeit zu fangen.

Konzepte erleichtern Vorlage Schnittstellen zu spezifizieren, die funktioniert über mehrere Instanziierungen . Dies ist bedeutsam, aber ein viel weniger drängendes Problem als Funktionsschnittstellen angeben, die über mehrere Anrufe funktionieren.

In Antwort auf Ihre Frage - eine formelle Aussage „Ich implementieren diese Schnittstelle“ einen großen Nachteil hat, dass es die Schnittstelle erfunden werden muss, bevor die Umsetzung ist. Typinferenz Systeme nicht, aber sie haben den großen Nachteil, dass Sprachen im Allgemeinen nicht die Gesamtheit einer Schnittstelle Arten zum Ausdruck bringen können, und so können Sie ein Objekt, das geschlossen wird, um den richtigen Typ zu sein, die aber nicht über die Semantik zu diesem Typ zugeschrieben. Wenn Ihre Sprache Schnittstellen an allen Adressen (insbesondere, wenn es um sie zu Klassen entspricht), dann AFAIK müssen Sie hier eine Haltung einnehmen, und Ihr Nachteil wählen.

[*] In der Regel. Es gibt einige Ausnahmen, zum Beispiel des C ++ Typ-System zur Zeit nicht, dass Sie von der Verwendung eines InputIterator nicht daran hindert, als ob es dich um einen Vorwärts-Iterator war. Sie müssen Iterator Merkmale dafür. Duck Typing allein macht Sie nicht aufhören, ein Objekt vorbei, das geht, schwimmt und quakt, aber bei näherem Hinsehen nicht wirklich tun alle diese Dinge, wie eine Ente tut, und ist erstaunt zu erfahren, dass man dachte, es würde ;-)

Andere Tipps

Schnittstellen nicht Vererbung basiert sein müssen ... das ist eine andere und eine separate Design-Entscheidung. Die neue Go Sprachschnittstellen haben, aber nicht ihr Erbe, zum Beispiel: „eine Art erfüllt automatisch jede Schnittstelle, eine Teilmenge ihrer Methoden“gibt an, wie die Go FAQ es ausdrückt. Simionato musings über Vererbung und Interfaces, aufgefordert durch die jüngste Veröffentlichung des Go, kann sein lesenswert.

ich einig, dass typeclasses ist noch leistungsfähiger, im Wesentlichen weil, wie abstrakt Basisklassen , lassen sie zusätzlich Sie nützlichen Code angeben (definiert eine zusätzliche Methode X in Bezug auf den anderen für alle Arten, die ansonsten die Basisklasse übereinstimmen, aber nicht definieren X selbst) - ohne das Erbe Gepäck, das ABCs (anders von Schnittstellen) führt fast zwangsläufig. Fast unvermeidlich, weil zum Beispiel Python ABCs „glauben“, dass sie Vererbung beinhalten, in Bezug auf die Konzeptualisierung sie bieten ... aber in der Tat, sie müssen nicht sein Erbe-basierte (viele sind nur das Vorhandensein und die Unterschrift bestimmter Methoden überprüft, wie Go-Schnittstellen).

Was, warum würde eine Sprache Designer (wie Guido, im Fall von Python) wählt solche „Wölfe im Schafspelz“ als Pythons ABCs, über den einfachen Haskell-like typeclasses, die ich seit dem im Jahr 2002 vorgeschlagen hatte, das ist eine schwierigere Frage zu beantworten. Immerhin, es ist nicht so, dass Python jeden Skrupel gegen Borgen Konzepte von Haskell hat (zum Beispiel Listenkomprehensionen / Generator Ausdrücke - Python braucht eine Dualität hier, während Haskell nicht den Fall ist, weil Haskell „faul“ ist). Die beste Hypothese ich anbieten kann, ist, dass jetzt, Vererbung so vertraut ist, die meisten Programmierer, die die meisten Sprachdesigner fühlen sie sich leichter Akzeptanz Dinge durch Gießen gewinnen können, die Art und Weise (obwohl Go-Designer müssen nicht zu tun, dass gelobt werden).

Lassen Sie mich fett: Ich verstehe vollkommen die Motivation es zu haben und kann die Motivation einiger Leute nicht verstehen, dagegen zu argumentieren ...

Was Sie wollen, ist nicht virtueller Ad-hoc-Polymorphismus.

  • Ad-hoc: Implementierung variieren kann
  • nicht virtuell: aus Performance-Gründen; compiletime Versand

Der Rest ist Zucker in meiner Meinung nach.

C ++ hat bereits Ad-hoc-Polymorphismus über Templates. „Konzepte“ würden jedoch klären, welche Art von Ad-hoc-polymorpher Funktionalität durch die Benutzer definierte Einheit verwendet wird.

C # hat einfach keine Möglichkeit, es zu tun. Ein Ansatz, der nicht nicht virtuell sein : Wenn Typen wie Schwimmer nur so etwas wie „INumeric“ umsetzen würde oder „IAddable“ (...) wir würden zumindest in der Lage eine allgemeine schreiben min, max, Lerp und auf der Grundlage dieser Klemme, maprange, Bezier (...). Allerdings wäre es nicht schnell sein. Sie nicht wollen, dass.

Möglichkeiten, dies zu fixieren: Da .NET JIT-Kompilierung funktioniert sowieso erzeugt auch anderen Code für List<int> als für List<MyClass> (aufgrund der unterschiedlichen Wert- und Referenztypen) wäre es wahrscheinlich hinzufügen, nicht so viel von einem Overhead auch anderen Code für die Ad-hoc-polymorphen Teile zu erzeugen. Die Sprache C # müßte nur einen Weg, es auszudrücken. Eine Möglichkeit ist, was Sie skizziert werden.

Eine andere Möglichkeit wäre, um Typ-Einschränkungen für die Funktion mit eine Ad-hoc-polymorphe Funktion hinzuzufügen:

    U SuperSquare<T, U>(T a) applying{ 
         nonvirtual operator (*) T (T, T) 
         nonvirtual Foo U (T)
    }
    {
        return Foo(a * a);
    }

Natürlich können Sie mit immer mehr Einschränkungen könnten am Ende, wenn Bar implementieren, die Foo verwendet. So können Sie einen Mechanismus wollen einen Namen mehrere Einschränkungen geben, die Sie regelmäßig verwenden ... Aber auch dies ist Zucker und eine Möglichkeit, sich ihr zu nähern wäre, nur das Konzept typeclass verwenden ...

zu geben ist, einen Namen mehr Einschränkungen wie eine Typklasse definieren, aber ich möchte nur als eine Art Abkürzung Mechanismus um es betrachten - Zucker für eine beliebige Sammlung von Funktionstyp Einschränkungen:

    // adhoc is like an interface: it is about collecting signatures
    // but it is not a type: it dissolves during compilation 
    adhoc AMyNeeds<T, U>
    {
         nonvirtual operator (*) T (T, T) 
         nonvirtual Foo U (T)
    } 

    U SuperSquare<T, U>(T a) applying AMyNeeds<T, U>        
    {
        return Foo(a * a);
    }

An einem bestimmten Ort „main“ all Art Argumente sind bekannt und alles wird konkret und gemeinsam erarbeitet werden.

Was fehlt noch, ist der Mangel unterschiedliche Implementierungen zu schaffen. Im oberen Beispiel we nur verwendet polymorphe Funktionen und alle wissen lassen ...

Die Umsetzung dann wieder könnte den Weg von Erweiterungsmethoden folgen - in ihrer Fähigkeit, Funktionalität zu jeder Klasse an jedem beliebigen Punkt hinzuzufügen:

 public static class SomeAdhocImplementations
 {
    public nonvirtual int Foo(float x)
    {
        return round(x);
    }
 }

Im Haupt jetzt können Sie schreiben:

    int a = SuperSquare(3.0f); // 3.0 * 3.0 = 9.0 rounded should return 9

Der Compiler überprüft all „nicht-virtuelle“ Ad-hoc-Funktionen finden sowohl einen eingebauten Schwimm (*) Operator und ein int Foo (float) und daher in der Lage, diese Zeile zu kompilieren.

Ad-hoc-Polymorphismus natürlich kommt mit dem Nachteil, dass Sie für jede Kompilierung Typ neu kompilieren, so dass die richtigen Implementierungen eingesetzt bekommen. Und wahrscheinlich IL nicht akzeptiert, dass in eine DLL gesetzt wird. Aber vielleicht haben sie daran arbeiten sowieso ...

Ich sehe keine wirkliche Notwendigkeit für Instanciation eines Typs Klassenkonstrukts. Wenn etwas auf Kompilierung fehlschlagen würden wir die Fehler der Einschränkungen oder wenn diejenigen erhalten wurden Boundled zusammen mit einer „Ad-hoc“ codeclock die Fehlermeldung könnte erhalten noch mehr lesbar.

    MyColor a = SuperSquare(3.0f); 
    // error: There are no ad hoc implementations of AMyNeeds<float, MyColor> 
    // in particular there is no implementation for MyColor Foo(float)

Aber natürlich auch die Instanziierung einer Typklasse / „Ad-hoc-Polymorphismus-Schnittstelle“ ist denkbar. Die Fehlermeldung würde dann heißt es: „The AMyNeeds constraint of SuperSquare has not been matched. AMyNeeds is available as StandardNeeds : AMyNeeds<float, int> as defined in MyStandardLib“. Es wäre auch möglich, die Umsetzung in einer Klasse zusammen mit anderen Methoden und fügen Sie die „Ad-hoc-Schnittstelle“ auf die Liste der unterstützten Schnittstellen.

Aber unabhängig von der jeweiligen Sprache Design: Ich sehe nicht den Nachteil sie das eine oder andere hinzuzufügen. Speichern Sie statisch typisierten Sprachen werden immer brauchen die Grenze von Ausdruckskraft zu drücken, da sie, indem sie zu wenig begonnen, was eine kleinere Gruppe von Ausdruckskraft einer Norm zu sein scheintal Programmierer hätte möglich sein, erwartet ...

TLDR: i auf Ihrer Seite bin. Sachen wie diese saugt Mainstream statisch typisierten Sprachen. Haskell zeigte den Weg.

  

Was ist die Argumentation gegen typeclasses?

Implementierungskomplexität für Compiler Schriftsteller ist immer ein Problem, wenn neue Sprache Funktionen. C ++ bereits diesen Fehler und wir haben schon Jahre Buggy C ++ Compiler als Folge erlitten.

  

Schnittstellen sind Vererbung basierte und somit relativ langsam aufgrund indirection und darüber hinaus gibt es keine Möglichkeit zu lassen, einen bestehenden Typ implementieren sie

Das stimmt nicht. Schauen Sie sich die OCaml strukturell typisierte Objekt-System, zum Beispiel:

# let foo obj = obj#bar;;
val foo : < bar : 'a; .. > -> 'a = <fun>

Die foo Funktion akzeptiert jedes Objekt jeglicher Art, die das notwendige bar Verfahren liefert.

Das Gleiche gilt für übergeordnetes Modul-System ML. Tatsächlich gibt es sogar eine formale Gleichwertigkeit zwischen diesem und Typklassen. In der Praxis sind Typklassen besser für klein Abstraktionen wie Betreiber Überlastung während höherer Ordnung Module besser für große Abstraktionen wie Okasaki der Parametrierung von catenable Listen über Warteschlangen.

  

Haben sie gravierende Nachteile, nachdem alle?

Sehen Sie sich Ihr eigenes Beispiel generische Arithmetik. F # verarbeiten kann eigentlich schon, dass bestimmten Fall dank der INumeric Schnittstelle. Der F # Matrix Typ selbst verwendet diesen Ansatz.

Allerdings haben Sie ersetzt nur den Code Maschine mit dynamischer Dispatch in eine separate Funktion hinzufügen, für arithmetische um Größenordnungen langsamer zu machen. Für die meisten Anwendungen ist das unnütz langsam. Sie können, indem ganze Programmoptimierungen dieses Problem lösen, aber das hat offensichtliche Nachteile. Darüber hinaus gibt es wenige Gemeinsamkeiten zwischen numerischen Methoden für int vs float aufgrund numerischer Robustheit so Ihre Abstraktion auch praktisch nutzlos ist.

Die Frage sollte sicher sein: kann jemand überzeugend machen für die Annahme von Typklassen

  

Aber immer noch keine "Mainstream-Sprache" bietet [Typklassen.]

Wenn diese Frage gestellt wurde, könnte dies wahr gewesen sein. Heute ist es ein viel stärkeres Interesse an Sprachen wie Haskell und Clojure. Haskell hat Typklassen (class / instance), Clojure 1.2+ hat Protokolle (defprotocol / extend).

  

Was ist die Argumentation gegen [Typklassen]?

Ich glaube nicht, dass die Typklassen objektiv „schlechter“ sind als andere Polymorphismus Mechanismen; sie folgen nur einem anderen Ansatz. So ist die eigentliche Frage ist, tun sie gut in eine bestimmte Programmiersprache passen?

Lassen Sie uns kurz überlegen, wie Typ-Klassen unterscheiden sich von Schnittstellen in Sprachen wie Java oder C #. In diesen Sprachen unterstützt eine Klasse nur Schnittstellen, die in dieser Klasse Definition ausdrücklich erwähnt und umgesetzt werden. Typklassen sind jedoch Schnittstellen, die später zu einer bereits definierten Art angehängt werden, auch in einem anderen Modul. Diese Art von Typ Erweiterbarkeit ist offensichtlich ganz anders als die Mechanismen in bestimmten „Mainstream“ OO-Sprachen.


Lassen Sie uns jetzt Typklassen für einige Mainstream-Programmiersprachen in Betracht ziehen.

Haskell :. Überflüssig zu erwähnen, dass diese Sprache hat Typklassen

Clojure . Wie oben erwähnt, hat Clojure etwas wie Typklassen in Form von Protokolle

C ++ . Wie Sie selbst gesagt haben, Konzepte von der C ++ 11-Spezifikation abgeworfen wurde

  

Im Gegenteil: Konzepte, die eine ganz analogical Idee sind, hat von der nächsten C ausgeschlossen ++

Ich habe nicht die ganze Debatte um diese Entscheidung folgt. Von dem, was ich gelesen habe, waren Konzepte nicht „ready noch“: Es gibt noch Karten Debatte über Konzept war. Allerdings wurden Konzepte nicht ganz aufgegeben, es wird erwartet, dass sie es in die nächste Version von C ++ machen.

C # : Mit Sprachversion 3 hat C # im Wesentlichen zu einem Hybrid der objektorientierten und funktionalen Programmierparadigmen. Eine Zugabe wurde auf die Sprache aus, die vom Konzept her sehr ähnlich ist Klassen zu geben: Erweiterungsmethoden . Der wesentliche Unterschied ist, dass Sie (scheinbar) Anbringen neue Methoden zu einem bestehenden Typ, nicht-Schnittstellen.

(Zugegeben, das Verlängerungsverfahren Mechanismus nicht annähernd so elegant wie Haskells instance … where Syntax ist. Extensions Methoden sind nicht „wirklich“ zu einer Art angebracht ist, werden sie als eine syntaktische Transformation umgesetzt. Am Ende jedoch dies nicht machen ein sehr großer praktischer Unterschied.)

Ich glaube nicht, dies in absehbarer Zeit geschehen wird - die Sprache Designer werden wahrscheinlich nicht einmal Erweiterung hinzufügen Eigenschaften auf die Sprache und die Erweiterung Schnittstellen würde sogar geht noch einen Schritt weiter als das.

( VB.NET : Microsoft wurde "co-entwickelt", um die C # und VB.NET Sprachen für einige Zeit, so dass meine Aussagen über C # für VB.NET gültig sein passieren, auch. )

Java : Ich weiß, dass Java nicht sehr gut, aber die Sprache C ++, C # und Java, es ist wahrscheinlich die "reinste" OO-Sprache. Ich sehe nicht, wie Typklassen natürlich in diese Sprache passen würden.

F # : Ich habe einen Forenbeitrag gefunden zu erklären warum Typklassen vielleicht nie in F # eingeführt bekommen. Diese Erklärung konzentriert sich auf die Tatsache, dass F # ein Nominativ hat, und kein Strukturtypsystem. (Obwohl ich nicht sicher bin, ob dies ein ausreichender Grund für F # nicht Typklassen zu haben.)

Versuchen Sie, eine Matroid definieren, das ist, was wir tun (logcally und nicht oral einen Matroid sagen), und es ist wahrscheinlich immer noch so etwas wie eine C-Struktur. Liskov Prinzip (letzte Turing Siegerin) bekommt zu abstrakt, zu kategorisch, zu theoretisch, weniger Behandlung von tatsächlichen Daten und mehr rein theoretisches Klasse-System, für handson pragmatische Problemlösung, blickten sie kurz die wie PROLOG, Code über Code zu Code zu Code sahen ... während ein Algorithmus Sequenzen und travelsals beschreibt verstehen wir auf Papier oder Tafel. Hängt das Ziel Sie haben, Problem mit minimalem Code oder abstrakteste zu lösen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top