Irgendein Vorteil der Verwendung von Typkonstruktoren in Typklassen?

8

Nehmen Sie zum Beispiel die Klasse Functor :

%Vor%

Hier ist Maybe ein Typkonstruktor.

Aber wir können das auf zwei andere Arten tun:

Erstens, Verwenden von Multi-Parameter-Klassen:

%Vor%

Zweitens mit Typfamilien:

%Vor%

Jetzt gibt es einen offensichtlichen Vorteil der beiden letzteren Methoden, nämlich dass es uns erlaubt, Dinge wie diese zu tun:

%Vor%

Oder:

%Vor%

So können wir mit monomorphen Containern arbeiten.

Der zweite Vorteil ist, dass wir Instanzen von Typen erstellen können, die nicht den Parameter type als letzten Parameter haben. Nehmen wir an, wir machen einen Either style type, aber setzen die Typen falsch herum:

%Vor%

Während

%Vor%

funktioniert gut und

%Vor%

ist auch gut.

Angesichts dieser Flexibilität Vorteile von nur die Verwendung von vollständigen Typen (keine Typ-Signaturen) in Typ-Klassen-Definitionen, gibt es einen Grund, die ursprüngliche Stildefinition zu verwenden, vorausgesetzt, Sie verwenden GHC und nichts dagegen, die Erweiterungen zu verwenden? Das heißt, gibt es etwas Spezielles, das Sie tun können, einen Typenkonstruktor zu setzen, nicht nur einen vollständigen Typ in einer Typklasse, die Sie nicht mit Multi-Parameter-Typklassen oder Typfamilien machen können?

    
Clinton 06.04.2015, 00:26
quelle

3 Antworten

12

In Ihren Vorschlägen werden einige wichtige Details zur vorhandenen Functor -Definition ignoriert, da Sie nicht die Einzelheiten des Schreibens über das, was mit der Memberfunktion der Klasse geschehen soll, durchgearbeitet haben.

%Vor%

Eine wichtige Eigenschaft von fmap ist momentan, dass das erste Argument Typen ändern kann. %Code%. Sie können das nicht einfach wegwerfen oder Sie verlieren den größten Teil des Wertes von fmap show :: (Functor f, Show a) => f a -> f String . Also wirklich, fmap müsste eher aussehen wie ...

%Vor%

Beachten Sie, wie unglaublich kompliziert es geworden ist, die Schlussfolgerung zumindest nahe möglich zu machen. Alle funktionalen Abhängigkeiten sind vorhanden, um eine Instanzauswahl zu ermöglichen, ohne überall Typen zu kommentieren. (Ich habe möglicherweise ein paar mögliche funktionale Abhängigkeiten darin verpasst!) Die Instanz selbst hat einige Gleichheitsbeschränkungen für verrückte Typen entwickelt, um die Auswahl von Instanzen zuverlässiger zu machen. Und der schlimmste Teil ist - das hat immer noch schlechtere Eigenschaften für die Argumentation als MultiFunctor .

Angenommen, meine vorherige Instanz existierte nicht, könnte ich eine Instanz wie folgt schreiben:

%Vor%

Das ist natürlich kaputt - aber es ist auf eine neue Art gebrochen, die vorher nicht einmal möglich war. Ein wirklich wichtiger Teil der Definition von fmap ist, dass die Typen Functor und a in b nirgendwo in der Instanzdefinition erscheinen. Ein Blick auf die Klasse genügt, um dem Programmierer mitzuteilen, dass das Verhalten von fmap nicht von den Typen fmap und a abhängig sein kann. Sie erhalten diese Garantie kostenlos. Sie müssen nicht darauf vertrauen, dass Instanzen korrekt geschrieben wurden.

Da b Ihnen diese Garantie kostenlos zur Verfügung stellt, müssen Sie bei der Definition einer Instanz nicht einmal beide fmap -Gesetze überprüfen. Es genügt, das Gesetz Functor zu überprüfen. Das zweite Gesetz kommt kostenlos, wenn das erste Gesetz bewiesen ist. Aber mit dem gebrochenen fmap id x == x , das ich gerade geliefert habe, ist mfmap wahr, obwohl das zweite Gesetz nicht ist.

Als Implementierer von mfmap id x == x haben Sie mehr zu tun, um zu beweisen, dass Ihre Implementierung korrekt ist. Als Benutzer müssen Sie der Korrektheit der Implementierung mehr vertrauen, da das Typsystem nicht so viel garantieren kann.

Wenn Sie ausführlichere Beispiele für die anderen Systeme ausarbeiten, stellen Sie fest, dass sie genauso viele Probleme haben, wenn Sie die volle Funktionalität von mfmap unterstützen möchten. Und deshalb werden sie nicht wirklich benutzt. Sie fügen ein Los der Komplexität für nur einen kleinen Nutzengewinn hinzu.

    
Carl 06.04.2015 13:06
quelle
4

Nun, zum einen ist die traditionelle Funktorklasse einfach viel einfacher. Das allein ist ein guter Grund, es vorzuziehen, obwohl dies Haskell und nicht Python ist. Und es stellt auch die mathematische Idee besser dar, was ein Funktor sein soll: ein Mapping von Objekten zu Objekten ( f :: *->* ), mit zusätzlicher Eigenschaft ( ->Constraint ), die jeweils ( forall (a::*) (b::*) ) Morphismus ( a->b ) wird auf einen Morphismus auf dem entsprechenden Objekt ( -> f a->f b ) gehoben. Das ist in der * -> * -> Constraint -Version der Klasse oder ihrer TypFamilies-Entsprechung nicht sehr deutlich zu sehen.

Auf einem praktischeren Konto, ja, es gibt auch Dinge, die Sie nur mit der (*->*)->Constraint Version tun können.

Insbesondere garantiert Ihnen diese Einschränkung sofort, dass alle Haskell-Typen gültige Objekte sind, die Sie in den Funktor einfügen können, während Sie für MultiFunctor jeden möglichen enthaltenen Typ überprüfen müssen, einen einzeln. Manchmal ist das nicht möglich (oder?), Wie wenn man unendlich viele Typen abbildet:

%Vor%

Beachten Sie, dass hierbei die Applicative -Instanz von f nicht nur für den Typ a , sondern auch für beliebige Tupel verwendet wird. Ich kann nicht sehen, wie Sie das mit einer MultiParamTypeClasses - oder TypeFamilies -basierten Anwendungsklasse ausdrücken könnten. (Es ist möglich, dass Sie Tough zu einem geeigneten GADT machen, aber ohne ... wahrscheinlich nicht.)

BTW, dieses Beispiel ist vielleicht nicht so nutzlos, wie es vielleicht aussieht - es drückt im Grunde genommen nur lesbare Vektoren der Länge 2 n in einem monadischen Zustand aus.

>     
leftaroundabout 06.04.2015 01:05
quelle
2

Die erweiterte Variante ist in der Tat flexibler. Es wurde z.B. von Oleg Kiselyov, eingeschränkte Monaden zu definieren. Grob kann man

haben %Vor%

ermöglicht die Parametrisierung von Monad-Instanzen über a und b . Dies ist nützlich, weil Sie diese Typen auf Mitglieder einer anderen Klasse beschränken können:

%Vor%

Beachten Sie, dass wir wegen der Ord Einschränkung% code_de% nicht mit unbeschränkten Monaden definieren können. In der Tat erfordert die Monad-Klasse, dass die Monade bei allen Typen verwendbar ist.

Siehe auch: parametrisierte (indizierte) Monade .

    
chi 06.04.2015 06:55
quelle

Tags und Links