Typ für "Wege, wie Werte unterschiedlich sein können"

8

Ich suche nach einem Konzept in der Typentheorie, von dem ich sicher bin, dass es wahrscheinlich untersucht wurde, aber den Namen dahinter nicht kenne.

Betrachten wir eine ML-ähnliche Sprache mit Produkt- und Summentypen und ein Hindley-Milner-ähnliches Typsystem. Ich werde die OCaml-Syntax verwenden.

Ich frage mich, wie zwei verschiedene Werte unterschiedlich sein können.

Verwenden

Mein Anwendungsfall ist das Schreiben von "klareren" Fehlermeldungen in eine xUnit-ähnliche Bibliothek: Wenn zwei Werte unterschiedlich sind, als erwartet wurde, dass sie gleich sind, kann dies dazu beitragen, eine klarere, bessere Meldung zu erstellen:

Vor:

Werte unterscheiden sich: erwartet {x = [1;2;3;4], y = "a long string"}, bekommen{x = [1;2;3;3], y = "a long string"}

Nach:

Werte unterscheiden sich: an Position .x[3], erwartet 4, bekam 3.

(Möglicherweise besteht eine Beziehung zu funktionalen Linsen, da wir letztendlich eine Linse mit einem kleineren Wert konstruieren, der sich unterscheidet.)

Produkttypen

Zum Beispiel:

type p =
  { x : int
  ; y : string
  }

Gleichheit kann definiert werden als:

let equal a b =
  equal_int a.x b.x && equal_string a.y b.y

Es ist aber auch möglich, die Unterschiede zu wiederholen:

type delta_p =
  | Equal
  | Diff_x of int * int
  | Diff_y of string * string

let diff_p a b =
  if not (equal_int a.x b.x) then
    Diff_x (a.x, b.x)
  else if not (equal_string a.y b.y) then
    Diff_y (a.y, b.y)
  else
    Equal

(Es kann sinnvoll sein, einen delta_intTyp zu definieren, um int * intzu zeigen, dass er rekursiv ist.)

Summentypen

Für einen Summentyp gibt es mehrere Möglichkeiten, sich zu unterscheiden: einen anderen Konstruktor oder einen anderen Wert

type s = X of int | Y of string

type ctor_s =
  | Ctor_X
  | Ctor_Y

type delta_s =
  | Equal
  | Diff_ctor of ctor_s * ctor_s
  | Diff_X of int * int
  | Diff_Y of string * string

let diff_s a b = match (a, b) with
  | X xa, X xb ->
    if equal_int xa xb then
      Equal
    else
      Diff_X (xa, xb)
  | (* Y case similar *)
  | X _, Y _ -> Diff_ctor (Ctor_X, Ctor_Y)
  | Y _, X _ -> Diff_ctor (Ctor_Y, Ctor_X)

Wie heißt dieses Konzept? Wo kann ich mehr darüber erfahren?

Vielen Dank!

Étienne Millon
quelle
2
Ein Reißverschluss ist ein "Zeiger" auf einen Ort. Ich weiß nicht, ob jemand Möglichkeiten studiert hat, dem Benutzer einen Reißverschluss zu zeigen.
Andrej Bauer
Es wurde nicht wirklich allgemein diskutiert, aber der Anwendungsfall erinnert mich an The View from the Left , Abschnitt 7, wo sie einen verifizierten Typprüfer erstellen, der den genauen Unterschied zwischen erwartetem und allgemeinem Typ meldet. Abbildung 15 hat den Isnt-Typ, der Ihrem Anwendungsfall entspricht.
Max New
Danke für die Hinweise. Es scheint mir, dass es bei Reißverschlüssen mehr darum geht, sich nach links und rechts zu "bewegen" als sich zu konzentrieren, aber das ist auch ein Effekt, den sie haben. Das scheint in der Tat ziemlich verwandt mit Sichtmustern zu sein.
Étienne Millon

Antworten:

9

Ich denke, Sie suchen nach einer typisierten Variante der Anti-Vereinigung . Anti-Unification kann wie folgt beschrieben werden. Nehmen wir zunächst an, wir haben eine Grammatik von Begriffen wie folgt:

t ::= () | (t, t) | C t | X 

Hier ()und (t, t)bezeichnet Einheiten und Paare, C tist ein Term mit einem führenden Konstruktor und Xeine Termvariable, die für jeden Term eingesetzt werden kann.

Das Anti-Vereinigungs-Problem besagt, wenn Sie zwei Begriffe angeben t1und t2was ist der am wenigsten allgemeine Begriff, tso dass es Substitutionen gibt s1und s2dass s1(t) = t1und s2(t) = t2.

Als konkretes Beispiel mit zwei Begriffen

t1 = Cons(3, Cons(2, Cons(1, Nil)))
t2 = Cons(1, Cons(2, Cons(3, Nil)))

Der Anti-Unification-Algorithmus würde den Anti-Unifier zurückgeben

t = Cons(X, Cons(2, Cons(Y, Nil)))

denn die ersetzungen s1 = [X:3, Y:1]und s2 = [X:1, Y:3]beantragten twürden dir t1und t2zurück geben. Nebenbei mussten wir "am wenigsten allgemein" angeben, weil sonst:

t' = Z 

mit den Auswechslungen s1 = [Z:t1]und s2 = [Z:t2]würde den Trick machen.

gen :Term×TermVar

antiunify ()       ()         = ()
antiunify (t1, t2) (t1', t2') = (antiunify t1 t1', antiunify t2 t2')
antiunify (C t)    (C t')     = C (antiunify t t')
antiunify t        t'         = gen(t, t') -- default: t and t' dissimilar 

Erweitern Sie diesen Algorithmus, um die beiden Substitutionen zurückzugeben, s1und s2ich gehe als Übung.

Neel Krishnaswami
quelle
Danke für den Begriff (Wortspiel beabsichtigt). Das sieht so aus, als könnte es sein. Eine typisierte Algebra von "Pfaden" wie diesen wäre großartig. Vielen Dank!
Étienne Millon