ML funktioniert von polymorphen Listen zu polymorphen Listen


8

Ich lerne Programmieren in ML (OCaml) und habe früher nach ML-Funktionen vom Typ'a -> 'b gefragt . Jetzt habe ich ein bisschen mit Funktionen vom Typ experimentiert 'a list -> 'b list. Es gibt einige offensichtliche einfache Beispiele:

let rec loop l = loop l
let return_empty l = []
let rec loop_if_not_empty = function [] -> []
                                   | l -> loop_if_not_empty l

Was ich nicht herausfinden kann, ist, wie man eine Funktion erstellt, die etwas anderes tut als die leere Liste oder Schleife zurückzugeben (ohne eine Bibliotheksfunktion zu verwenden). Kann das gemacht werden? Gibt es eine Möglichkeit, nicht leere Listen zurückzugeben?

Bearbeiten: Ja, wenn ich eine Funktion vom Typ habe 'a -> 'b, kann ich eine andere oder eine Funktion vom Typ 'a list -> 'b listerstellen, aber ich frage mich hier, wie ich die erste erstellen soll.


1
Wie bei der vorherigen Frage richten Sie sich bitte in Ihrer Antwort an die Lernprogrammierung des CS101-Schülers und nicht an den Typentheoretiker, zu dem Ihre Antwort ihn später inspirieren könnte.
Gilles 'SO - hör auf böse zu sein'

Beachten Sie, dass wenn Sie eine Funktion f mit diesem Typ hätten, die eine nicht leere Liste zurückgibt, Spaß a -> List.hd (f [a]) den Typ 'a ->' b hätte, ohne eine Ausnahme zu beenden oder auszulösen.
Gallais

Antworten:


6

Nun, etwas, das als Parametrizität bekannt ist, sagt uns, dass es, wenn wir die reine Teilmenge von ML betrachten (dh keine unendliche Rekursion refund all das seltsame Zeug), keine andere Möglichkeit gibt, eine Funktion mit diesem Typ zu definieren, als die, die das Leere zurückgibt Liste.

Alles begann mit Wadlers Artikel „ Theorems for free! ”. Dieses Papier sagt uns im Grunde zwei Dinge:

  1. Wenn wir Programmiersprachen betrachten, die bestimmte Bedingungen erfüllen, können wir einige coole Theoreme ableiten, indem wir nur die Typensignatur einer polymorphen Funktion betrachten (dies wird als Parametrizitätssatz bezeichnet).
  2. ML (ohne unendliche Rekursion refund all das seltsame Zeug) erfüllt diese Bedingungen.

Aus dem Parametrizität Theorem wissen wir , dass , wenn wir eine Funktion haben f : 'a list -> 'b list, dann für alle 'a, 'b, 'c, 'dund für alle Funktionen g : 'a -> 'c, die h : 'b -> 'dwir haben:

map h ∘ f = f ∘ map g

(Beachten Sie, flinks hat Typ 'a list -> 'b listund frechts ist 'c list -> 'd list.)

Wir können frei wählen, was gwir wollen, also lassen Sie 'a = 'cund g = id. Jetzt haben wir map id = id(leicht durch Induktion der Definition von zu beweisen map):

map h ∘ f = f

Jetzt lass 'b = 'd = boolund h = not. Nehmen wir für einige an zs : bool list, dass es passiert f zs ≠ [] : bool list. Es ist klar, dass map not ∘ f = fdas nicht gilt, weil

(map not ∘ f) zs ≠ f zs

Wenn das erste Element der Liste rechts ist true, dann ist links das erste Element falseund umgekehrt!

Dies bedeutet, dass unsere Annahme falsch ist und f zs = []. Sind wir fertig? Nein.

Wir gingen davon aus, dass 'bist bool. Wir haben gezeigt , dass , wenn fmit Typ aufgerufen wird f : 'a list -> bool listfür jede 'a, fmuss immer die leere Liste zurück. Kann es sein, dass wenn wir anrufen f, f : 'a list -> unit listes etwas anderes zurückgibt? Unsere Intuition sagt uns, dass dies Unsinn ist: Wir können einfach keine reine ML-Funktion schreiben, die immer die leere Liste zurückgibt, wenn wir möchten, dass sie uns eine Liste von Booleschen Werten gibt, und ansonsten möglicherweise eine nicht leere Liste zurückgibt! Dies ist jedoch kein Beweis.

Was wir sagen wollen, ist, dass fes einheitlich ist : Wenn es immer die leere Liste für zurückgibt bool list, muss es die leere Liste für unit listund im Allgemeinen für jede zurückgeben 'a list. Genau darum geht es beim zweiten Punkt in der Aufzählungsliste am Anfang meiner Antwort.

Das Papier sagt uns , dass in ML fnehmen muß damit verbundene Werte zu verwandt denjenigen. Ich werde nicht in die Details über die Beziehungen, ist es genug , um zu sagen , dass Listen werden im Zusammenhang , wenn und nur wenn sie die gleiche Länge haben und ihre Elemente sind paarweise im Zusammenhang (das heißt, [x_1, x_2, ..., x_m]und [y_1, y_2, ..., y_n]beziehen sich, wenn und nur wenn m = nund x_1bezieht sich auf y_1und x_2ist verwandt mit y_2und so weiter). Und der Spaß daran ist, in unserem Fall, da fpolymorph ist, wir können definieren , jede Beziehung auf die Elemente von Listen!

Lassen Sie uns holen jeder 'a, 'bund schauen Sie sich f : 'a list -> 'b list. Nun sieh dir an f : 'a list -> bool list; Wir haben bereits gezeigt, dass in diesem Fall fimmer die leere Liste zurückgegeben wird. Wir postulieren nun, dass alle Elemente von 'asich auf sich selbst beziehen (denken Sie daran, wir können jede gewünschte Beziehung wählen), dies impliziert, dass sich jede zs : 'a listauf sich selbst bezieht. Wie wir wissen, werden fverwandte Werte mit verwandten Werten in Verbindung gebracht. Dies bedeutet, dass sie f zs : 'b listverwandt sind f zs : bool list, aber die zweite Liste hat eine Länge von Null, und da die erste Liste damit verwandt ist, ist sie auch leer.


Der Vollständigkeit halber möchte ich erwähnen, dass es in der Originalarbeit des Wadlers einen Abschnitt über die Auswirkungen der allgemeinen Rekursion (mögliche Nichtbeendigung) gibt, und es gibt auch eine Arbeit , die freie Theoreme in Gegenwart von untersucht seq.


Jetzt vermute ich, dass der Beweis in einem Schritt gh
erbracht werden

Nitpick, Parametrizität beginnt nicht mit Wadlers Artikel (der behauptet, eine Zusammenfassung der Ansätze zur Definition von Parametrizität zu sein). Die Idee geht auf Reynolds Artikel "Typen, Abstraktion und parametrischer Polymorphismus" zurück. Die Idee war meines Wissens auch in Girards Normalisierungsnachweis für System F enthalten.
Daniel Gratzer

4

Kehren wir zu einfacheren Objekten zurück: Sie können kein geeignetes Objekt vom Typ erstellen, 'ada dies bedeuten würde, dass dieses Objekt xüberall dort verwendet werden kann, wo 'aes passt. Und das heißt überall: als Ganzzahl, als Array, sogar als Funktion. Zum Beispiel , das würde bedeuten , man Dinge tun kann , wie x+2, x.(1)und (x 5). Typen existieren genau , um dies zu verhindern.

Dies ist die gleiche Idee, die für eine Funktion vom Typ gilt 'a -> 'b, aber es gibt einige Fälle, in denen dieser Typ existieren kann: Wenn die Funktion niemals ein Objekt vom Typ zurückgibt 'b: Wenn eine Ausnahme wiederholt oder ausgelöst wird.

Dies gilt auch für Funktionen, die eine Liste zurückgeben. Wenn Ihre Funktion vom Typ ist t -> 'b listund Sie ein Objekt vom Typ erstellen tund auf diese Funktion anwenden, bedeutet dies, dass Sie bei erfolgreichem Zugriff auf ein Element dieser Liste auf ein Objekt mit allen Typen zugreifen. Sie können also auf kein Element der Liste zugreifen: Die Liste ist entweder leer oder ... es gibt keine Liste.

Der Typ 'a list -> 'b listerscheint jedoch in üblichen Übungen, aber das ist nur, wenn Sie bereits eine Funktion des Typs haben 'a -> 'b:

let rec map (f : 'a -> 'b) =
  function
  | [] -> []
  | x :: xs -> f x :: map f xs

Aber Sie kennen diesen wahrscheinlich.

val map : ('a -> 'b) -> 'a list -> 'b list

1
Der ältere Typentheoretiker ist von dieser Antwort weniger als begeistert. Ok, ein nicht leerer Typvariablenkontext ist eine Möglichkeit, eine Funktion zu haben, die buchstäblich vom Typ 'a -> 'boder ist 'a list -> 'b list, aber das ist keine so interessante Beobachtung. Tatsächlich werde ich die Frage bearbeiten, um klar zu machen, dass dies nicht das ist, worüber sich der jüngere Schüler beim Programmieren Gedanken gemacht hat.
Gilles 'SO - hör auf böse zu sein'

Aber der ältere Typentheoretiker weiß, dass ML nicht logisch fehlerhaft ist. Wenn Sie eine Funktion f : 'a list -> 'b listund eine tsolche erzeugen können , f t <> []wird dieses Programm die Typprüfung durchführen, kann jedoch weitaus schlechter abschneiden als das Auslösen einer Ausnahme : let y = List.hd (f t) in (y y) (y + y.(0) + y.(0).(0)).
jmad

2

Parametrizitätssatz aus den "Theorems for Free!" Das Papier sagt uns, dass ML-Begriffe eine ganz besondere Eigenschaft haben: Wenn wir den Typ eines Begriffs als Beziehung zu Werten dieses Typs betrachten, wird der Wert dieses Begriffs mit sich selbst in Beziehung gesetzt. So sehen Sie Typen als Beziehungen an:

  • Ein Funktionstyp 'a -> 'bentspricht der Beziehung, die definiert wird, indem gesagt wird, dass zwei Funktionen verwandt sind, wenn sie verwandte Werte mit verwandten Werten annehmen (vorausgesetzt 'aund 'beinigen Beziehungen entsprechen).
  • Ein Listentyp 'a listentspricht der Beziehung, die definiert wird, indem gesagt wird, dass zwei Listen miteinander verbunden sind, wenn sie dieselbe Länge haben und ihre übereinstimmenden Elemente miteinander verbunden sind (vorausgesetzt, dies 'aentspricht einer Beziehung).
  • (Nun der interessanteste Teil.) Ein polymorpher Typ entspricht der Beziehung, die definiert wird, indem gesagt wird, dass zwei polymorphe Werte zusammenhängen, wenn wir zwei beliebige Typen auswählen können , jede Beziehung zwischen den Elementen dieser Typen, alle Instanzen der Typvariablen durch diese ersetzen Beziehung, und die resultierenden Werte werden weiterhin in Beziehung gesetzt.

foo : 'a -> 'afooA1A2Aa1:A1a2:A2Afooa1fooa2A

a1Aa2fooa1Afooa2.

Af:A1A2

f(a1)=a2f(fooa1)=fooa2,

oder mit anderen Worten:

f(fooa1)=foo(f(a1)),

Das ist genau der freie Satz für die idFunktion : f . id = id . f.


foo : 'a list -> 'b listA1A2AB1B2BA1A2B1B2

Jetzt verwenden wir dies, um zu beweisen, dass für zwei beliebige Typen Aund Bdie Funktion fooeine leere Liste für jede Eingabe zurückgibt as : A list.

  • A1=A2= AAA
  • B1= ØB2= BB
  • asist mit sich selbst verwandt (wie wir die Identitätsbeziehung gewählt haben A), ist also foo as : Ø listverwandt mit foo as : B list.
  • Wir wissen, dass zwei Listen nur verknüpft werden können, wenn ihre Längen gleich sind, und wir wissen auch, dass die erste der resultierenden Listen leer sein muss, da es keine Elemente des ØTyps geben kann.

Daher muss für jeden A, Bund as : A listwir haben, foo as : B listdas muss eine leere Liste sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.