Bedeutung isomorpher Funktionen

74

Kurze Frage: Welche Bedeutung haben isomorphe Funktionen bei der Programmierung (nämlich bei der funktionalen Programmierung)?

Lange Frage: Ich versuche, einige Analoga zwischen funktionaler Programmierung und Konzepten in der Kategorietheorie zu ziehen, die auf einem Teil der Umgangssprache basieren, die ich von Zeit zu Zeit höre. Im Wesentlichen versuche ich, diesen Jargon in etwas Konkretes "auszupacken", das ich dann erweitern kann. Ich werde dann in der Lage sein, den Jargon mit einem Verständnis dafür zu verwenden, wovon zum Teufel ich spreche. Welches ist immer schön.

Einer dieser Begriffe, den ich ständig höre, ist Isomorphismus . Ich nehme an, es geht darum, über die Äquivalenz zwischen Funktionen oder Funktionszusammensetzungen nachzudenken. Ich habe mich gefragt, ob jemand einige Einblicke in einige gängige Muster geben könnte, bei denen die Eigenschaft des Isomorphismus nützlich ist (bei der funktionalen Programmierung), und für alle gewonnenen Nebenprodukte, wie z. B. Compiler-Optimierungen aus Überlegungen zu isomorphen Funktionen.

ThaDon
quelle
Das ist eine gute Frage. Erstellen Sie eine Reihe von ihnen? In diesem Fall sollten Sie die Links irgendwo sammeln und auf die Sammlung in Ihrem Profil verweisen.
Marcin
2
@Marcin Ich bin in der Tat, ich werde Sie wissen lassen, wenn ich sie alle zusammengestellt habe
ThaDon
3
Nicht nur eine gute Frage, sondern auch eine großartige Verwendung von StackOverflow!
Jörg W Mittag
iso-bedeutet gleich (/ äquivalent): en.wiktionary.org/wiki/iso-#Etymology und -morph-bedeutet Form / Form: thefreedictionary.com/morph . Ein Isomorphismus ist also die Beziehung / Funktion der Äquivalenz zwischen zwei Typen. Wie man einen definieren kann, wird im Folgenden bereits sehr, sehr gut erklärt.
Cetin Sert

Antworten:

81

Ich habe ein kleines Problem mit der positiv bewerteten Antwort für Isomorphismus, da die kategorietheoretische Definition von Isomorphismus nichts über Objekte aussagt. Um zu sehen, warum, überprüfen wir die Definition.

Definition

Ein Isomorphismus ist ein Paar von Morphismen (dh Funktionen), fund zwar gso, dass:

f . g = id
g . f = id

Diese Morphismen werden dann "Iso" -Morphismen genannt. Viele Leute verstehen nicht, dass sich der "Morphismus" im Isomorphismus auf die Funktion und nicht auf das Objekt bezieht. Sie würden jedoch sagen, dass die Objekte, die sie verbinden, "isomorph" sind, was die andere Antwort beschreibt.

Beachten Sie, dass die Definition des Isomorphismus nicht sagt, was ( .) idoder sein =muss. Die einzige Voraussetzung ist, dass sie, was auch immer sie sind, auch die Kategoriengesetze erfüllen:

f . id = f
id . f = f
(f . g) . h = f . (g . h)

Die Zusammensetzung (dh ( .)) verbindet zwei Morphismen zu einem Morphismus und idbezeichnet eine Art "Identitäts" -Übergang. Das heißt, wenn sich unsere Isomorphismen zum Identitätsmorphismus aufheben, idkönnen Sie sie als Umkehrungen voneinander betrachten.

Für den speziellen Fall, in dem die Morphismen Funktionen sind, wird dann idals Identitätsfunktion definiert:

id x = x

... und Zusammensetzung ist definiert als:

(f . g) x = f (g x)

... und zwei Funktionen sind Isomorphismen, wenn sie sich beim Erstellen der Identitätsfunktion aufheben id.

Morphismen gegen Objekte

Es gibt jedoch mehrere Möglichkeiten, wie zwei Objekte isomorph sein können. Zum Beispiel bei den folgenden zwei Typen:

data T1 = A | B
data T2 = C | D

Es gibt zwei Isomorphismen zwischen ihnen:

f1 t1 = case t1 of
    A -> C
    B -> D
g1 t2 = case t2 of
    C -> A
    D -> B

(f1 . g1) t2 = case t2 of
    C -> C
    D -> D
(f1 . g1) t2 = t2
f1 . g1 = id :: T2 -> T2

(g1 . f1) t1 = case t1 of
    A -> A
    B -> B
(g1 . f1) t1 = t1
g1 . f1 = id :: T1 -> T1

f2 t1 = case t1 of
    A -> D
    B -> C
g2 t2 = case t2 of
    C -> B
    D -> A

f2 . g2 = id :: T2 -> T2
g2 . f2 = id :: T1 -> T1

Deshalb ist es besser, den Isomorphismus anhand der spezifischen Funktionen zu beschreiben, die die beiden Objekte betreffen, als anhand der beiden Objekte, da es möglicherweise nicht unbedingt ein eindeutiges Funktionspaar zwischen zwei Objekten gibt, das die Isomorphismusgesetze erfüllt.

Beachten Sie außerdem, dass es nicht ausreicht, wenn die Funktionen invertierbar sind. Beispielsweise sind die folgenden Funktionspaare keine Isomorphismen:

f1 . g2 :: T2 -> T2
f2 . g1 :: T2 -> T2

Auch wenn beim Verfassen keine Informationen verloren f1 . g2gehen, kehren Sie nicht in Ihren ursprünglichen Zustand zurück, selbst wenn der endgültige Zustand denselben Typ hat.

Außerdem müssen Isomorphismen nicht zwischen konkreten Datentypen liegen. Hier ist ein Beispiel für zwei kanonische Isomorphismen, die nicht zwischen konkreten algebraischen Datentypen liegen und stattdessen einfach Funktionen in Beziehung setzen: curryund uncurry:

curry . uncurry = id :: (a -> b -> c) -> (a -> b -> c)
uncurry . curry = id :: ((a, b) -> c) -> ((a, b) -> c)

Verwendung für Isomorphismen

Kodierung der Kirche

Eine Verwendung von Isomorphismen besteht darin, Datentypen als Funktionen in der Kirche zu kodieren. Zum Beispiel Boolist isomorph zu forall a . a -> a -> a:

f :: Bool -> (forall a . a -> a -> a)
f True  = \a b -> a
f False = \a b -> b

g :: (forall a . a -> a -> a) -> Bool
g b = b True False

Überprüfen Sie das f . g = idund g . f = id.

Der Vorteil von Church-Codierungsdatentypen besteht darin, dass sie manchmal schneller ausgeführt werden (da Church-Codierung ein Continuation-Passing-Stil ist) und in Sprachen implementiert werden können, die überhaupt keine Sprachunterstützung für algebraische Datentypen bieten.

Implementierungen übersetzen

Manchmal versucht man, die Implementierung einer Funktion einer Bibliothek mit der Implementierung einer anderen Bibliothek zu vergleichen. Wenn Sie beweisen können, dass sie isomorph sind, können Sie beweisen, dass sie gleich leistungsfähig sind. Die Isomorphismen beschreiben auch, wie eine Bibliothek in die andere übersetzt wird.

Beispielsweise gibt es zwei Ansätze, mit denen eine Monade aus der Signatur eines Funktors definiert werden kann. Eine ist die freie Monade, die vom freePaket bereitgestellt wird , und die andere ist die operative Semantik, die vom operationalPaket bereitgestellt wird .

Wenn Sie sich die beiden Kerndatentypen ansehen, sehen sie unterschiedlich aus, insbesondere ihre zweiten Konstruktoren:

-- modified from the original to not be a monad transformer
data Program instr a where
    Lift   :: a -> Program instr a
    Bind   :: Program instr b -> (b -> Program instr a) -> Program instr a
    Instr  :: instr a -> Program instr a

data Free f r = Pure r | Free (f (Free f r))

... aber sie sind tatsächlich isomorph! Das bedeutet, dass beide Ansätze gleich leistungsfähig sind und jeder in einem Ansatz geschriebene Code mithilfe der Isomorphismen mechanisch in den anderen Ansatz übersetzt werden kann.

Isomorphismen, die keine Funktionen sind

Isomorphismen sind auch nicht auf Funktionen beschränkt. Sie sind tatsächlich für jede definiert Categoryund Haskell hat viele Kategorien. Aus diesem Grund ist es sinnvoller, in Morphismen als in Datentypen zu denken.

Beispielsweise bildet der LensTyp (von data-lens) eine Kategorie, in der Sie Linsen zusammenstellen und eine Identitätslinse haben können. Mit unserem obigen Datentyp können wir also zwei Linsen definieren, die Isomorphismen sind:

lens1 = iso f1 g1 :: Lens T1 T2
lens2 = iso g1 f1 :: Lens T2 T1

lens1 . lens2 = id :: Lens T1 T1
lens2 . lens1 = id :: Lens T2 T2

Beachten Sie, dass zwei Isomorphismen im Spiel sind. Einer ist der Isomorphismus, der zum Aufbau jeder Linse (dh f1und g1) verwendet wird (und deshalb wird diese Konstruktionsfunktion auch genannt iso), und dann sind die Linsen selbst auch Isomorphismen. Es ist zu beachten, dass in der obigen Formulierung die verwendete Zusammensetzung ( .) keine Funktionszusammensetzung, sondern eine Linsenzusammensetzung ist und idnicht die Identitätsfunktion, sondern die Identitätslinse:

id = iso id id

Das heißt, wenn wir unsere beiden Linsen zusammensetzen, sollte das Ergebnis nicht von dieser Identitätslinse zu unterscheiden sein.

Gabriel Gonzalez
quelle
Es würde helfen, den Isomorphismus zwischen Programund zu zeigen Free; Ich kann es nicht auf einen Blick herausfinden.
Kühlkörper
Program fist isomorph zu Free (Coyoneda f)und Coyoneda fisomorph zu f, weshalb die beiden Typen isomorph sind. Ich habe es weggelassen, weil der Beweis etwas komplizierter ist.
Gabriel Gonzalez
Ich wünschte, ich hätte verstanden, was mit dem Programm los war. Ist das eine gültige Definition? Welche Rolle spielen Instrumente? Ich vermute, dies könnte mit einer Bearbeitung für verwirrende Tippfehler zu tun haben, die ich gerne sehen würde, aber nicht ganz herausfinden kann, wie ich es selbst machen soll.
Schweinearbeiter
@pigworker Ich habe das von kopiert operational, aber die Basismonade entfernt, um sie zu einer gewöhnlichen Monade anstelle eines Monadentransformators zu machen. Dabei habe ich vergessen, das Ts aus den Konstruktoren zu entfernen , aber ich habe es behoben.
Gabriel Gonzalez
@GabrielGonzalez Und welchen Zweck erfüllt das Instrument?
Schweinearbeiter
25

Ein Isomorphismus u :: a -> b ist eine Funktion, die eine Umkehrung hat , dh eine andere Funktion, v :: b -> a so dass die Beziehungen

u . v = id
v . u = id

sind zufrieden. Sie sagen, dass zwei Typen isomorph sind, wenn zwischen ihnen ein Isomorphismus besteht. Dies bedeutet im Wesentlichen, dass Sie sie als denselben Typ betrachten können - alles, was Sie mit dem einen tun können, können Sie mit dem anderen tun.

Isomorphismus von Funktionen

Die beiden Funktionstypen

(a,b) -> c
a -> b -> c

sind isomorph, da wir schreiben können

u :: ((a,b) -> c) -> a -> b -> c
u f = \x y -> f (x,y)

v :: (a -> b -> c) -> (a,b) -> c
v g = \(x,y) -> g x y

Sie können das überprüfen u . vund v . usind beides id. In der Tat sind die Funktionen uund vbesser unter den Namen curryund bekannt uncurry.

Isomorphismus und Newtypes

Wir nutzen den Isomorphismus immer dann aus, wenn wir eine Newtype-Deklaration verwenden. Zum Beispiel ist der zugrunde liegende Typ der Staatsmonade s -> (a,s), über den man etwas verwirrend nachdenken kann. Durch Verwendung einer Newtype-Deklaration:

newtype State s a = State { runState :: s -> (a,s) }

Wir erzeugen einen neuen Typ, State s ader isomorph ist s -> (a,s)und der deutlich macht, wenn wir ihn verwenden. Wir denken über Funktionen nach, die einen veränderbaren Zustand haben. Wir bekommen auch einen praktischen Konstruktor Stateund einen Getter runStatefür den neuen Typ.

Monaden und Comonaden

Eine erweiterte Sicht betrachtet der Isomorphismus mit curryund uncurrydass ich oben verwendet. Der Reader r aTyp hat die newtype-Deklaration

newType Reader r a = Reader { runReader :: r -> a }

Im Zusammenhang mit Monaden hat eine Funktion f, die einen Leser erzeugt, daher die Typensignatur

f :: a -> Reader r b

das ist äquivalent zu

f :: a -> r -> b

Das ist die Hälfte des Curry / Uncurry-Isomorphismus. Wir können auch den CoReader r aTyp definieren :

newtype CoReader r a = CoReader { runCoReader :: (a,r) }

was zu einer Comonade gemacht werden kann. Dort haben wir eine Funktion cobind oder =>>eine Funktion, die einen Coreader übernimmt und einen Rohtyp erzeugt:

g :: CoReader r a -> b

das ist isomorph zu

g :: (a,r) -> b

Aber wir haben das bereits gesehen a -> r -> bund (a,r) -> bsind isomorph, was uns eine nicht triviale Tatsache gibt: Die Lesermonade (mit monadischer Bindung) und die Coreader-Comonade (mit comonadischer Kobinde) sind ebenfalls isomorph! Insbesondere können beide für den gleichen Zweck verwendet werden - die Bereitstellung einer globalen Umgebung, die durch jeden Funktionsaufruf geführt wird.

Chris Taylor
quelle
Hm? Ich habe den Kompositionsoperator '.' Vergessen. Was ist das nochmal? Verkettung? .. noo
13

Denken Sie in Datentypen. In Haskell können Sie sich beispielsweise zwei Datentypen als isomorph vorstellen, wenn zwei Funktionen vorhanden sind, die Daten auf einzigartige Weise zwischen ihnen transformieren. Die folgenden drei Typen sind isomorph zueinander:

data Type1 a = Ax | Ay a
data Type2 a = Blah a | Blubb
data Maybe a = Just a | Nothing

Sie können sich die Funktionen, die sich zwischen ihnen transformieren, als Isomorphismen vorstellen. Dies passt zur kategorischen Idee des Isomorphismus. Wenn zwischen Type1und mit Type2zwei Funktionen fund gmit existieren f . g = g . f = id, sind die beiden Funktionen Isomorphismen zwischen diesen beiden Typen (Objekten).

ertes
quelle
5
Nur um hinzuzufügen: Der Fachbegriff für Funktionen wie fund gist eine Bijektion.
Huon
2
Mit können mapSie auch Isomorphismen zwischen verschiedenen Arten von Listen erstellen. Gleiches gilt für fmapund für anwendbare Typen.
Riccardo T.
2
Datentypen sind also isomorph, wenn zwischen ihnen eine bijektive Beziehung besteht?
Marcin
@ertes so id wäre im folgenden Fall x? func = f . g...func x
ThaDon