Da einige Typdefinitionen:Warum überprüft diese Haskell-Code-Typ mit fundeps aber einen unantastbaren Fehler mit Typfamilien?
data A
data B (f :: * -> *)
data X (k :: *)
... und das typeclass:
class C k a | k -> a
... diese (sehr gekünstelt im Sinne eines minimalen Beispiel) Funktionsdefinitionen typecheck:
f :: forall f. (forall k. (C k (B f)) => f k) -> A
f _ = undefined
g :: (forall k. (C k (B X)) => X k) -> A
g = f
Wenn wir jedoch eine Typfamilie anstelle einer Klasse mit einer funktionalen Abhängigkeit verwenden:
type family F (k :: *)
... dann die entsprechenden Funktionsdefinitionen nicht typecheck:
f :: forall f. (forall k. (F k ~ B f) => f k) -> A
f _ = undefined
g :: (forall k. (F k ~ B X) => X k) -> A
g = f
• Couldn't match type ‘f0’ with ‘X’
‘f0’ is untouchable
inside the constraints: F k ~ B f0
bound by a type expected by the context:
F k ~ B f0 => f0 k
Expected type: f0 k
Actual type: X k
• In the expression: f
In an equation for ‘g’: g = f
I Abschnitt 5.2 der the OutsideIn(X) paper lesen, die berührbaren und unberührbaren Typ Variablen, und ich Art beschreibt verstehe, was hier vor sich geht. Wenn ich ein zusätzliches Argument f
hinzufügen, die die Wahl der f
außerhalb des inneren forall
drückt, dann geht das Programm typechecks:
f :: forall f a. f a -> (forall k. (F k ~ B f) => f k) -> A
f _ _ = undefined
g :: forall a. X a -> (forall k. (F k ~ B X) => X k) -> A
g = f
Doch was speziell hat mich so verwirrt, in diesem Beispiel ist, warum die funktionale Abhängigkeit verschiedener hat Verhalten. Ich habe gehört, dass Leute zu verschiedenen Zeiten behaupten, funktionale Abhängigkeiten wie diese seien äquivalent zu einer Typfamilie plus einer Gleichheit, aber dies zeigt, dass das nicht wirklich wahr ist.
Welche Informationen liefert die funktionale Abhängigkeit in diesem Fall, die f
in einer Weise instanziiert werden kann, die die Typenfamilie nicht enthält?
Beachten Sie, dass 'g = f @ X 'auch Typ überprüft. Es scheint, dass der Inferenzalgorithmus sich nicht verpflichtet, die Typvariable "f" als "X" zu wählen. Ich kann nicht sehen, warum - normalerweise, weil es einen anderen Wert von "f" geben könnte, der den Typ "(für k (Fk ~ B f) => fk) -> A" gleich "(forall k . (Fk ~ BX) => Xk) -> A'. Hier scheint 'f ~ X' die einzige Lösung für mich zu sein (oder?). Interessant. – chi
@chi Ich denke schon, aber ich weiß nicht genug über diesen speziellen Fall des Typcheckers, um sicher einen Fehler zu öffnen. Vielleicht sollte ich sowieso ein Ticket öffnen, und wenn es Verhalten ist, werde ich zumindest wahrscheinlich wissen warum? –
Interessant in der Tat! Ich habe jetzt zweimal meine Meinung darüber geäußert, ob das eigentlich eine Überprüfung mit _neither_ fundeps nicht geben soll, Familien oder nur mit fundeps oder mit beiden. Ich verstehe einfach nicht gut genug, wie die Einschränkungen gelöst sind, um zu entscheiden.Aber ich halte es zumindest nicht für plausibel, dass nur die fundep-Version funktionieren sollte: Der entscheidende Unterschied scheint zu sein, dass Typklassen mit ihren Superklassen "entwirrt" werden können (das 'f' wird aus' Bf' extrahiert), aber aus einer Gleichheitsbedingung ist dies nicht möglich. – leftaroundabout