Das Anzeigen von 'newtype T a = T (a - Int)' ist ein Typenkonstruktor, der kein Functor ist

8

Es wurde behauptet, dass newtype T a = T (a -> Int) ein Typkonstruktor ist, der kein Funktor ist (aber ein kontravarianter Funktor). Wie das? Oder was ist der kontravariante Funktor (woher nehme ich an, dass es offensichtlich ist, warum dies nicht zu einem normalen Funktor gemacht werden kann)?

    
George 22.05.2017, 02:24
quelle

3 Antworten

6

Gegeben

%Vor%

Versuchen wir, die Contravariant Instanz für diesen Datentyp zu erstellen.

Hier ist die Typklasse in Frage:

%Vor%

Grundsätzlich ist contramap ähnlich wie fmap , aber anstatt eine Funktion a -> b auf f a -> f b zu heben, wird sie auf f b -> f a angehoben.

Beginnen wir mit dem Schreiben der Instanz ...

%Vor%

Bevor wir ? ausfüllen, überlegen wir, was die Typen g und f sind:

%Vor%

Und aus Gründen der Klarheit könnten wir das auch erwähnen

%Vor%

So können wir das ? wie folgt ausfüllen:

%Vor%

Um super pedantisch zu sein, könnten Sie g als aToB und f als bToInt umbenennen.

%Vor%

Der Grund, warum Sie eine Contravariant -Instanz für T a schreiben können, ist die Tatsache, dass a in kontravarianter Position in T (a -> Int) steht. Der beste Weg, um sich davon zu überzeugen, dass T a nicht Functor ist, ist es, die Functor -Instanz selbst zu schreiben (und nicht zu schreiben).

    
liminalisht 22.05.2017, 04:17
quelle
6

Angenommen, T ist ein Funktor. Dann haben wir

%Vor%

Nun versuchen wir es zu implementieren.

%Vor%

Wir fangen eindeutig mit so etwas an und kombinieren die Werte f , g und x irgendwie, um einen Wert für y vom richtigen Typ zu berechnen. Wie können wir das machen? Nun, beachte, dass ich es _y genannt habe, was GHC sagt, dass ich Hilfe brauche, um herauszufinden, was ich dort hinlege. Was hat GHC zu sagen?

%Vor%

Also jetzt sind wir uns über die Arten von allem relevanten klar, oder? Wir müssen irgendwie ein Int zurückgeben, und wir müssen es aufbauen:

%Vor%

Nun, okay, das einzige, was wir haben können, ist möglicherweise Int ist g , also lassen Sie uns das ausfüllen, lassen Sie g 's Argument leer, um GHC nach mehr Hilfe zu fragen:

%Vor%

Okay, wir hätten das selbst vorhersagen können: Um g zu nennen, brauchen wir irgendwo einen Wert vom Typ a . Aber wir haben keine Werte vom Typ a im Bereich, und wir haben auch keine Funktionen, die einen Wert vom Typ a ausgeben! Wir stecken fest: Es ist unmöglich, einen Wert des Typs zu produzieren, den wir jetzt wollen, obwohl wir bei jedem Schritt das einzig mögliche getan haben: Es gibt nichts, was wir sichern und anders ausprobieren könnten.

Warum ist das passiert? Denn wenn ich dir eine Funktion vom Typ a -> Int gebe und sag "aber nebenbei, hier ist eine Funktion von a -> b , gib mir bitte eine Funktion von b -> Int zurück", kannst du eigentlich nicht verwenden die Funktion von a -> b , denn niemand gibt dir jemals a s, um sie aufzurufen! Wenn ich dir stattdessen eine Funktion von b -> a gegeben hätte, wäre das sehr hilfreich, oder? Sie könnten dann eine Funktion von b -> Int erzeugen, indem Sie zuerst die Funktion b -> a aufrufen, um eine a zu erhalten, und dann die ursprüngliche Funktion a -> Int aufrufen, um die gewünschte Int herauszuholen.

Und darum geht es bei einem kontravarianten Funktor: Wir kehren den Pfeil in der an fmap übergebenen Funktion um, damit er über Dinge, die Sie "brauchen" (Funktionsargumente), statt über Dinge, die Sie "haben" (konkrete Werte) Rückgabewerte von Funktionen usw.).

Nebenbei: Ich behauptete früher, dass wir "die einzig mögliche Sache" bei jedem Schritt getan hatten, was ein bisschen eine Frechheit war. Wir können keine Int aus f , g und x erstellen, aber natürlich können wir alle möglichen Zahlen aus der Luft zusammensetzen. Wir wissen nichts über den Typ b , also können wir nicht eine Int erhalten, die von dieser in irgendeiner Weise abgeleitet ist, aber wir könnten einfach sagen "lasst uns immer Null zurückgeben", und dies erfüllt technisch die Typüberprüfung :

%Vor%

Offensichtlich sieht das ziemlich falsch aus, da es scheint, dass f und g ziemlich wichtig sein sollten und wir ignorieren sie! Aber es prüft, also sind wir in Ordnung, oder?

Nein, das verletzt eines der Functor-Gesetze:

%Vor%

Wir können das leicht genug beweisen:

%Vor%

Und jetzt haben wir wirklich alles versucht: Die einzige Möglichkeit, ein Int zu erstellen, ohne unseren b -Typ überhaupt zu benutzen, ist, es aus nichts zu machen, und so weiter Verwendungen werden isomorph zur Verwendung von const sein, was gegen die Functor-Gesetze verstößt.

    
amalloy 22.05.2017 04:09
quelle
5

Hier ist noch ein bisschen Perspektive. Wie liminish zeigt, ist T Contravariant . Was können wir über Typen sagen, die sowohl kovariant als auch kontravariant sind?

%Vor%

Die ersten beiden Implementierungen sind im Grunde gleich ( change1' ist eine Optimierung von change1 ); jeder von ihnen verwendet die Tatsache, dass () ein "terminales Objekt" von Hask ist. change2 verwendet stattdessen die Tatsache, dass Void ein "initiales Objekt" ist.

Jede dieser Funktionen ersetzt alle a s in f a mit b s, ohne etwas über a , b oder die Beziehung zwischen ihnen zu wissen, so dass alles andere gleich bleibt. Es sollte wohl klar sein, dass f a nicht wirklich von a abhängt. Das heißt, der Parameter f muss ein Phantom sein. Das ist nicht der Fall für T , daher kann es nicht auch kovariant sein.

    
dfeuer 22.05.2017 17:41
quelle

Tags und Links