Ich habe kürzlich eine Frage zu syntactic-2.0 bezüglich der Definition von gestellt share
. Ich habe dies in GHC 7.6 arbeiten lassen :
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}
import Data.Syntactic
import Data.Syntactic.Sugar.BindingT
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
sup ~ Domain b, sup ~ Domain a,
Syntactic a, Syntactic b,
Syntactic (a -> b),
SyntacticN (a -> (a -> b) -> b)
fi)
=> a -> (a -> b) -> b
share = sugarSym Let
GHC 7.8 möchte -XAllowAmbiguousTypes
jedoch mit dieser Signatur kompilieren. Alternativ kann ich die ersetzen fi
mit
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
Welches ist der Typ, der vom Fundep auf impliziert wird SyntacticN
. Dadurch kann ich die Erweiterung vermeiden. Das ist natürlich so
- Ein sehr langer Typ, der zu einer bereits großen Signatur hinzugefügt werden kann
- lästig manuell abzuleiten
- unnötig wegen der fundep
Meine Fragen sind:
- Ist dies eine akzeptable Verwendung von
-XAllowAmbiguousTypes
? - Wann sollte diese Erweiterung im Allgemeinen verwendet werden? Eine Antwort hier schlägt vor, "es ist fast nie eine gute Idee".
Obwohl ich die Dokumente gelesen habe , habe ich immer noch Probleme zu entscheiden, ob eine Einschränkung nicht eindeutig ist oder nicht. Betrachten Sie insbesondere diese Funktion aus Data.Syntactic.Sugar:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f sugarSym = sugarN . appSym
Es scheint mir, dass
fi
(und möglicherweisesup
) hier mehrdeutig sein sollte, aber es kompiliert ohne die Erweiterung. Warum istsugarSym
eindeutig, währendshare
ist? Dashare
es sich um eine Anwendung von handeltsugarSym
,share
kommen alle Einschränkungen direkt vonsugarSym
.
sugarSym Let
, bei dem es(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
sich um mehrdeutige Typvariablen handelt und nicht?share
, aber tut der Kompilierung , wenn eine der Signaturen in der Frage erwähnt verwendet wird.f
ergibt sich, dass allein ausreicht, um vollständig zusig
unterscheidenfi
, undsup
.SyntacticN
macht infi
eindeutigsugarSym
, aber warum gilt das Gleiche nicht fürfi
inshare
?Antworten:
Ich sehe keine veröffentlichte Version der Syntaktik, deren Signatur
sugarSym
genau diese Typnamen verwendet. Daher verwende ich den Entwicklungszweig bei Commit 8cfd02 ^ , der letzten Version, in der diese Namen noch verwendet wurden.Warum beschwert sich GHC über die
fi
Signatur in Ihrem Typ, aber nicht über die fürsugarSym
? In der Dokumentation, auf die Sie verlinkt haben, wird erläutert, dass ein Typ nicht eindeutig ist, wenn er nicht rechts von der Einschränkung angezeigt wird, es sei denn, die Einschränkung verwendet funktionale Abhängigkeiten, um den ansonsten nicht mehrdeutigen Typ von anderen nicht mehrdeutigen Typen abzuleiten. Vergleichen wir also die Kontexte der beiden Funktionen und suchen nach funktionalen Abhängigkeiten.Also für
sugarSym
die nicht-mehrdeutig Typen sindsub
,sig
undf
, und von denen , sollten wir in der Lage sein, funktionale Abhängigkeiten zu folgen , um alle anderen Arten im Kontext verwendet eindeutig zu machen, nämlichsup
undfi
. Und tatsächlich verwendet dief -> internal
funktionale Abhängigkeit inSyntacticN
unseref
, um unsere zu disambiguierenfi
, und danach dief -> sig sym
funktionale Abhängigkeit inApplySym
verwendet unsere neu eindeutige, umfi
zu disambiguierensup
(undsig
, was bereits nicht mehrdeutig war). Das erklärt also, warumsugarSym
dieAllowAmbiguousTypes
Erweiterung nicht erforderlich ist .Schauen wir uns jetzt an
sugar
. Das erste, was mir auffällt, ist, dass sich der Compiler nicht über einen mehrdeutigen Typ beschwert, sondern über überlappende Instanzen:Wenn ich das richtig lese, ist es nicht so, dass GHC denkt, dass Ihre Typen mehrdeutig sind, sondern dass GHC bei der Überprüfung, ob Ihre Typen mehrdeutig sind, auf ein anderes, separates Problem gestoßen ist. Es sagt Ihnen dann, dass, wenn Sie GHC angewiesen hätten, die Mehrdeutigkeitsprüfung nicht durchzuführen, dieses separate Problem nicht aufgetreten wäre. Dies erklärt, warum das Aktivieren von AllowAmbiguousTypes das Kompilieren Ihres Codes ermöglicht.
Das Problem mit den überlappenden Instanzen bleibt jedoch bestehen. Die beiden von GHC (
SyntacticN f fi
undSyntacticN (a -> f) ...
) aufgelisteten Instanzen überschneiden sich. Seltsamerweise scheint sich die erste davon mit jeder anderen Instanz zu überschneiden, was verdächtig ist. Und was heißt[overlap ok]
das?Ich vermute, dass Syntactic mit OverlappingInstances kompiliert wurde. Und wenn man sich den Code ansieht , tut es das tatsächlich.
Wenn man ein bisschen experimentiert, scheint es, dass GHC mit überlappenden Instanzen einverstanden ist, wenn klar ist, dass eine streng allgemeiner ist als die andere:
Aber GHC ist nicht in Ordnung mit überlappenden Fällen, in denen keiner eindeutig besser passt als der andere:
Ihre Typensignatur wird verwendet
SyntacticN (a -> (a -> b) -> b) fi
und passt wederSyntacticN f fi
nochSyntacticN (a -> f) (AST sym (Full ia) -> fi)
besser als die andere. Wenn ich diesen Teil Ihrer Typensignatur inSyntacticN a fi
oder ändere,SyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
beschwert sich GHC nicht mehr über die Überlappung.Wenn ich Sie wäre, würde ich mir die Definition dieser beiden möglichen Instanzen ansehen und feststellen, ob eine dieser beiden Implementierungen die gewünschte ist.
quelle
Ich habe festgestellt, dass dies
AllowAmbiguousTypes
sehr praktisch istTypeApplications
. Betrachten Sie die FunktionnatVal :: forall n proxy . KnownNat n => proxy n -> Integer
von GHC.TypeLits .Um diese Funktion zu nutzen, könnte ich schreiben
natVal (Proxy::Proxy5)
. Ein alternativer Stil istTypeApplications
:natVal @5 Proxy
. Der Typ vonProxy
wird von der Typanwendung abgeleitet, und es ist ärgerlich, ihn jedes Mal schreiben zu müssen, wenn Sie anrufennatVal
. So können wir aktivierenAmbiguousTypes
und schreiben:Beachten Sie jedoch, dass Sie nicht mehr zurückkehren können , wenn Sie einmal mehrdeutig sind !
quelle