Warum wird dieser Code mit UndecidableInstances kompiliert und anschließend eine Endlosschleife zur Laufzeit generiert?

Beim Schreiben von Code mitUndecidableInstances Vorhin bin ich auf etwas gestoßen, das ich sehr merkwürdig fand. Ich habe es geschafft, versehentlich Code zu erstellen, der typüberprüft, obwohl ich der Meinung war, dass dies nicht der Fall sein sollte:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}

data Foo = Foo

class ConvertFoo a b where
  convertFoo :: a -> b

instance (ConvertFoo a Foo, ConvertFoo Foo b) => ConvertFoo a b where
  convertFoo = convertFoo . (convertFoo :: a -> Foo)

evil :: Int -> String
evil = convertFoo

Speziell dieconvertFoo Funktionstypchecks bei gegebenemirgendei Eingabe zu produzierenirgendei Ausgabe, wie durch die @ demonstrieevil Funktion. Zuerst dachte ich, dass ich es vielleicht aus Versehen geschafft habe, @ zu implementiereunsafeCoerce, aber das stimmt nicht ganz: Ich versuche tatsächlich, mein @ aufzurufeconvertFoo function (indem man so etwas wie @ macevil 3, zum Beispiel) geht einfach in eine Endlosschleife.

I sort von verstehe, was in einem vagen Sinne los ist. Mein Verständnis des Problems ist ungefähr so:

Die Instanz vonConvertFoo das ich definiert habe arbeitet anirgendei Eingabe und Ausgabe,a undb, nur begrenzt durch die zwei zusätzlichen Einschränkungen, die Konvertierungsfunktionen für @ haben müssa -> Foo undFoo -> b. Irgendwie kann diese Definition mit allen Eingabe- und Ausgabetypen übereinstimmen, sodass der Aufruf von @ anscheineconvertFoo :: a -> Foo wählt die Definition vonconvertFoo selbst, da es sowieso das einzige ist, das verfügbar ist.Schon seitconvertFoo ruft sich unendlich auf, die Funktion geht in eine Endlosschleife, die niemals endet.Schon seitconvertFoo wird nie beendet, der Typ dieser Definition ist bottom, alsotechnisc Keiner der Typen wird jemals verletzt, und das Programm überprüft die Schreibweise.

Nun, auch wenn das obige Verständnis korrekt ist, bin ich immer noch verwirrt darüber, warum das ganze Programm typecheckt. Konkret würde ich das @ erwartConvertFoo a Foo undConvertFoo Foo b Einschränkungen, die fehlschlagen sollen, sofern keine solchen Instanzen vorhanden sind.

I tu Verstehen Sie (zumindest unübersichtlich), dass Einschränkungen beim Auswählen einer Instanz keine Rolle spielen. Die Instanz wird ausschließlich anhand des Instanzkopfs ausgewählt. Anschließend werden die Einschränkungen überprüft. Ich konnte also feststellen, dass diese Einschränkungen aufgrund meinesConvertFoo a b Instanz, die ungefähr so tolerant ist, wie es nur sein kann. Dann müssten jedoch die gleichen Einschränkungen gelöst werden. Ich denke, dies würde den Typechecker in eine Endlosschleife versetzen, was dazu führen würde, dass GHC entweder beim Kompilieren hängen bleibt oder mir einen Stapelüberlauffehler ausgibt (letzterer ist mir aufgefallen) Vor)

Klar, aber der Typechecker machtnich Schleife, weil es glücklich Bottoms und mein Code glücklich kompiliert. Warum? Wie wird der Instanzkontext in diesem speziellen Beispiel erfüllt? Warum erhalte ich dadurch keinen Tippfehler oder erzeuge keine Typüberprüfungsschleife?

Antworten auf die Frage(4)

Ihre Antwort auf die Frage