Bitte bestätigen oder korrigieren Sie meine „englische Interpretation“ dieses Haskell -Code -Snippets

StackOverflow https://stackoverflow.com/questions/775730

Frage

Ich bin ein C# -Entwickler, der durcharbeitet "Real World Haskell" Um die funktionale Programmierung wirklich zu verstehen, so dass ich, wenn ich f#lerne, wirklich greife und nicht nur "C#Code in F#schreibe", sozusagen.

Nun, heute bin ich auf ein Beispiel gestoßen, von dem ich dachte, ich habe drei verschiedene Mal verstanden, nur um etwas zu sehen, das ich verpasst habe, meine Interpretation aktualisiert und wiederholt (und auch fluch, glauben Sie mir).

Jetzt glaube ich, dass ich es tatsächlich verstehe, und ich habe unten eine detaillierte "englische Interpretation" geschrieben. Können Sie Haskell Gurus bitte dieses Verständnis bestätigen oder darauf hinweisen, was ich verpasst habe?

HINWEIS: Der Haskell -Code -Snippet (direkt aus dem Buch zitiert) definiert einen benutzerdefinierten Typ, der als isomorph für den integrierten in Haskell -Listentyp sein.

Der Haskell -Code -Snippet

data List a = Cons a (List a)
              | Nil
              defining Show

Bearbeiten: Nach einigen Antworten sehe ich ein Missverständnis, das ich gemacht habe, bin aber nicht ganz klar über die Haskell -Parsen -Regeln, die diesen Fehler korrigieren würden. Ich habe also meine ursprüngliche (falsche) Interpretation unten aufgenommen, gefolgt von einer Korrektur, gefolgt von der Frage, die mir immer noch unklar bleibt.

Bearbeiten: Hier ist meine ursprüngliche (falsche) "englische Interpretation" des Snippets

  1. Ich definiere einen Typ namens "Liste".
  2. Der Listentyp ist parametrisiert. Es hat einen einzelnen Typparameter.
  3. Es gibt 2 Wertekonstruktoren, mit denen Listeninstanzen vorgenommen werden können. Ein Wertkonstruktor wird als "NIL" bezeichnet und der andere Wertkonstruktor "Nachteile" genannt.
  4. Wenn Sie den "NIL" -Wwertkonstruktor verwenden, gibt es keine Felder.
  5. Der "Cons" -Wertzwertkonstruktor hat einen einzelnen Typparameter.
  6. Wenn Sie den "Cons" -Wertzwertkonstruktor verwenden, müssen 2 Felder bereitgestellt werden. Das erste erforderliche Feld ist eine Instanz der Liste. Das zweite erforderliche Feld ist eine Instanz von a.
  7. (Ich habe absichtlich etwas über die "Definition der Show" weggelassen, weil es nicht Teil dessen ist, worauf ich mich gerade konzentrieren möchte).

Die korrigierte Interpretation wäre wie folgt (Änderungen in Fettdruck)

  1. Ich definiere einen Typ namens "Liste".
  2. Der Listentyp ist parametrisiert. Es hat einen einzelnen Typparameter.
  3. Es gibt 2 Wertekonstruktoren, mit denen Listeninstanzen vorgenommen werden können. Ein Wertkonstruktor wird als "NIL" bezeichnet und der andere Wertkonstruktor "Nachteile" genannt.
  4. Wenn Sie den "NIL" -Wwertkonstruktor verwenden, gibt es keine Felder.

    5. (Diese Zeile wurde gelöscht ... es ist nicht genau) Der "Cons" -Wertzwertkonstruktor hat einen einzelnen Typparameter.

  5. Wenn Sie den "Cons" -Wertzwertkonstruktor verwenden, müssen 2 Felder bereitgestellt werden. Das erste erforderliche Feld ist eine Instanz von a. Das zweite erforderliche Feld ist eine Instanz von "List-of-a".

  6. (Ich habe absichtlich etwas über die "Definition der Show" weggelassen, weil es nicht Teil dessen ist, worauf ich mich gerade konzentrieren möchte).

Die Frage, die noch unklar ist

Die anfängliche Verwirrung in Bezug auf den Teil des Snippets mit der Aufschrift "Cons a (Liste a)". Tatsächlich ist das der Teil, der mir immer noch unklar ist.

Die Leute haben darauf hingewiesen, dass jeder Gegenstand in der Leitung nach dem "Nachts" -Token a ist Typ, kein Wert. Das bedeutet also, dass diese Zeile lautet: "Der Cons Value Constructor hat 2 Felder: eine vom Typ 'a' und die andere vom Typ 'List-of-a'."

Das ist sehr hilfreich zu wissen. Etwas ist jedoch noch unklar. Wenn ich Instanzen mit dem Cons Value Constructor erstelle, interpretieren diese Instanzen "das erste 'a' als Bedeutung" Platzieren Sie den hier übergebenen Wert ". Aber sie tun es nicht Interpretieren Sie das zweite "a" genauso.

Betrachten Sie beispielsweise diese GHCI -Sitzung:

*Main> Cons 0 Nil
Cons 0 Nil
*Main> Cons 1 it
Cons 1 (Cons 0 Nil)
*Main> 

Wenn ich "cons 0 nil" eingeben, verwendet es den "Cons" -Wwertkonstruktor, um eine Instanz der Liste zu erstellen. Aus 0 erfährt es, dass der Typparameter "Integer" ist. Bisher keine Verwirrung.

Wie auch immer, es Auch bestimmt, dass die Wert des ersten Feldes der Nacht nichts über die Wert des zweiten Feldes ... es bestimmt nur, dass das zweite Feld a hat Typ von "List Integer".

Meine Frage ist also, warum bedeutet "a" im ersten Feld "der Typ dieses Feldes ist 'a' und Der Wert dieses Feldes ist 'a' ", während" a "im zweiten Feld bedeutet nur "Der Typ dieses Feldes ist 'Liste eines'"?

EDIT: Ich glaube, ich habe jetzt das Licht dank mehrerer Antworten gesehen. Lass es mich hier artikulieren. (Und wenn es irgendwie ist still In irgendeiner Weise falsch, bitte auf jeden Fall lassen Sie es mich wissen!)

Im Snippet "Cons a (Liste a)" sagen wir, dass der "cons" -Walterkonstruktor zwei Felder hat und dass das erste Feld vom Typ "a" ist und dass das zweite Feld vom Typ "vom Typ" ist "von a '.

Das ist alles, was wir sagen! Insbesondere sagen wir NICHTS Über Werte! Dies ist ein wichtiger Punkt, den ich vermisst habe.

Später möchten wir eine Instanz unter Verwendung des "Cons" -Wertzwertkonstruktors erstellen. Wir geben dies in den Dolmetscher ein: "cons 0 nil". Dies ausdrücklich Gibt den Cons Value Constructor an, 0 für den Wert des ersten Feldes zu verwenden und NIL als Wert für das zweite Feld zu verwenden.

Und das ist alles, was es gibt. Sobald Sie wissen, dass die Definition der Wertkonstruktorin angibt Nichts als Typen, alles wird klar.

Vielen Dank an alle für die hilfreichen Antworten. Und wie gesagt, wenn noch etwas ausgeschaltet ist, erzählen Sie mir bitte auf jeden Fall davon. Vielen Dank.

War es hilfreich?

Lösung

  • Der "Cons" -Wertzwertkonstruktor hat einen einzelnen Typparameter.

Nein: Sie haben es bereits parametriiert, als Sie deklariert haben data List a. Eine wirksame Eigenschaft davon ist, dass ich sie nicht mit einem Nil :: -Listen -Zeichen austauschen kann, wenn ich eine Nil :: -Liste habe.

  • Wenn Sie den "Cons" -Wertzwertkonstruktor verwenden, müssen 2 Felder bereitgestellt werden. Das erste erforderliche Feld ist eine Instanz der Liste. Das zweite erforderliche Feld ist eine Instanz von a.

Sie haben es ausgetauscht: Das erste erforderliche Feld ist eine Instanz von A, das zweite Feld ist eine Instanz der Liste.

Dieses Kapitel von Real World Haskell kann von Interesse sein.

Vielen Dank. Das ist das Kapitel, in dem ich gerade bin. Also ... als der Code "cons a (Liste a)" sagt, dachte ich, dass der "cons a" Teil davon feststellte, dass der Konstruktor des Konsumwerts parametrisiert wurde. Sie haben die Syntax für parametrisierte Typen noch nicht abgedeckt. Daher habe ich vermutet, dass die Syntax "A" erneut beherrscht muss, wenn Sie a verwenden möchten. Aber Sie sagen, das ist nicht notwendig? Und deshalb bedeutet das nicht das "a"?

Nö. Sobald wir einen Parameter in unserem Typ deklarieren, können wir ihn ansonsten wiederverwenden, um zu sagen: "Dieser Typ sollte dort verwendet werden." Es ist ein bisschen wie eine a -> b -> a Typ Signatur: A parameterisiert den Typ, aber dann muss ich das gleiche A wie der Rückgabewert verwenden.

OK, aber das ist verwirrend. Es scheint, dass das erste "a" bedeutet "das erste Feld ist eine Instanz eines", ",", ",", ",", ",", ",", ",

Nein, das ist nicht WAHR. Dies bedeutet nur, dass der Datentyp über einen Typ A parametrisiert.

Und es bedeutet auch, dass "das erste Feld den gleichen Wert hat wie der Wert, den sie für ein" übergeben haben ". Mit anderen Worten, es gibt Typ und Wert an.

Nein, das ist auch nicht wahr.

Hier ist ein lehrreiches Beispiel, dessen Syntax Sie zuvor gesehen haben können oder nicht:

foo :: Num a => a -> a

Dies ist eine ziemlich Standardsignatur für eine Funktion, die eine Nummer nimmt und etwas tut und Ihnen eine andere Nummer gibt. Was ich mit "einer Zahl" in Haskell-Speak tatsächlich meine, ist ein willkürlicher Typ "A", der die "Num" -Klasse implementiert.

So pariert dies dem Engländer:

Lassen Sie einen Typ angeben, der die NUM -Typeclass implementiert, und die Signatur dieser Methode ist ein Parameter mit dem Typ A und der Rückgabewert des Typs A.

Ähnliches passiert bei Daten.

Mir fällt auch ein, dass die Instanz der Liste in der Spezifikation von Nachteilen Sie auch verwirrt: Seien Sie sehr vorsichtig, wenn Sie analysieren: während der Nachteile einen Konstruktor angibt, was im Grunde ein Muster ist, in das Haskell die Daten einwickeln wird ((( Liste a) Sieht aus wie ein Konstruktor, ist aber einfach nur ein Typ, wie int oder doppelt. A ist ein Typ, kein Wert im Sinne des Begriffs.

Bearbeiten: Als Antwort auf die neueste Bearbeitung.

Ich denke, eine Dissektion wird zuerst gefordert. Dann werde ich mit Ihren Fragen Punkt für Punkt umgehen.

Haskell -Datenkonstruktoren sind etwas seltsam, da Sie die Konstruktor -Signatur definieren und kein anderes Gerüst erstellen müssen. Datatypen in Haskell haben keinen Vorstellung von Mitgliedsvariablen. (Hinweis: Es gibt eine alternative Syntax, für die diese Denkweise mehr zugänglich ist, aber lassen Sie uns das vorerst ignorieren.)

Eine andere Sache ist, dass Haskell -Code dicht ist; Seine Typsignatur ist so. Erwarten Sie also, dasselbe Symbol in verschiedenen Kontexten wiederverwendet wird. Die Typ -Inferenzierung spielt auch hier eine große Rolle.

Also zurück zu Ihrem Typ:

data List a = Cons a (List a)
              | Nil

Ich beschließe das in mehrere Stücke:

data Liste a

Dies definiert den Namen des Typs und alle parametrisierten Typen, die es später haben wird. Beachten Sie, dass Sie diese nur in anderen Typ -Signaturen angezeigt werden.

Nachteile a (List a) |
Null

Dies ist der Name des Datenkonstruktors. Dies ist kein Typ. Wir können jedoch Muster übereinstimmen, ALA:

foo :: List a -> Bool
foo Nil = True

Beachten Sie, wie Liste A der Typ in der Signatur ist, und NIL ist sowohl der Datenkonstruktor als auch das "Ding", für das wir übereinstimmen.

Cons a (Liste a)

Dies sind die Arten der Werte, die wir in den Konstruktor einfügen. CONS hat zwei Einträge, einer ist vom Typ A und einer von Typ List a.

Meine Frage ist also, warum bedeutet "a" im ersten Feld "Der Typ dieses Feldes ist 'a' und der Wert dieses Feldes ist 'a'", während "A" im zweiten Feld nur "den Typ bedeutet" Von diesem Feld ist 'Liste eines' '?

Einfach: Stellen Sie es nicht als uns vor, den Typ anzugeben. Denken Sie daran, dass Haskell den Typ daraus schlägt. Für unsere Absichten und Zwecke kleben wir einfach eine 0 hinein und einen Nil im zweiten Abschnitt. Dann betrachtet Haskell unseren Code und denkt:

  • Hmm, ich frage mich, was die Art der Nachteile 0 nil ist
  • Nun, Cons ist ein Datenkonstruktor für die Liste a. Ich frage mich, was die Art der Liste A ist
  • Nun, A wird im ersten Parameter verwendet, da der erste Parameter ein INT ist (eine weitere Vereinfachung; 0 ist eigentlich eine seltsame Sache, die als num typisiert wird), also ist das Mittel a num eine num
  • Hey, na ja, das bedeutet auch, dass die Art der Nullliste int ist, obwohl es dort nichts gibt, was das tatsächlich sagen würde

(Beachten Sie, dass es nicht so implementiert ist. Haskell kann viele seltsame Dinge tun, während die Inferenztypen in der Inferenzung sind. Dies ist teilweise der Grund, warum die Fehlermeldungen saugen.)

Andere Tipps

Analogien fehlen normalerweise in allen möglichen Arten, aber da Sie wissen, dass C# ich dachte, dies könnte hilfreich sein.

So würde ich das beschreiben List a Definition in C#, vielleicht beseitigt dies einige Dinge (oder wahrscheinlicher, verwirrt Sie noch mehr).

class List<A>
{
}

class Nil<A> : List<A>
{
    public Nil() {}
}

class Cons<A> : List<A>
{
    public A Head;
    public List<A> Tail;

    public Cons(A head, List<A> tail)
    {
        this.Head = head;
        this.Tail = tail;
    }
}

Wie du sehen kannst;

  • das List Der Typ hat einen einzelnen Typparameter (<A>),
  • das Nil Der Konstruktor hat keine Parameter,
  • und die Cons Der Konstruktor hat zwei Parameter, einen Wert head vom Typ A und ein Wert tail vom Typ List<A>.

Nun, in Haskell die Nil und Cons sind nur Konstruktoren für die List a Datentyp, in C# sind sie auch Typen an und für sich, also schlägt die Analogie fehl.

Aber ich hoffe, das gibt Ihnen ein intuitives Gefühl dafür, was die unterschiedlich sind A's repräsentieren.

(Und bitte kommentieren Sie, wie dieser schreckliche Vergleich Haskells Datentypen nicht gerecht wird.)

Cons a (List a)

Das erste Feld eines Nachteils ist ein Wert des Typs "a"Die zweite ist ein Wert des Typs"List a", dh eine Liste parametrisiert mit dem gleichen Typ wie der Parameter der aktuellen Liste.

5 ist falsch und ich würde 6 wie folgt sagen, um beide zu ersetzen:

Cons {1} a {2} (Liste a) {3} ist ein Konstruktor namens Cons (Teil vor {1}) für einen Wert vom Typ Liste a (die Datenliste a Teil), der zwei Werte benötigt: einen vom Typ A. (Der Teil zwischen {1} und {2}) und einer Typliste A (dem Teil zwischen {2} und {3}).

Um Ihnen bei einer offensichtlichen Quelle der Verwirrung zu helfen: In Haskell müssen Sie kaum explizite Typparameter angeben - Typ -Inferenz färbt die Typen aus Ihren Werten. In gewisser Weise, ja, wenn Sie einen Wert an eine Funktion oder einen Konstruktor übergeben, geben Sie auch einen Typ an, nämlich. der Typ des übergegangenen Wertes.

Ja, die Datensyntax ist ein wenig verwirrend, weil sie Namen und Typen Wortspiele und nicht wirklich macht syntaktisch Unterscheidung zwischen ihnen. Insbesondere in einer Konstruktordefinition wie:

Cons a (List a)

Das erste Wort ist der Name des Konstruktors; Jedes andere Wort ist der Name eines voranschließenden Typs. Also beides a und List a sind bereits im Bereich (die a wurde von der in den Geltungsbereich gebracht a in "data List a"), und Sie sagen, dass dies die Arten der Parameter sind. Ihre Rolle könnte besser nachgewiesen werden, indem Sie dasselbe angeben Datensatzsyntax:

Cons { headL :: a, tailL :: List a }

Dh ein Wert des Typs List Int, wenn Es wurde mit dem konstruiert Cons Konstruktor hat zwei Felder: eine Int und ein List Int. Wenn es mit konstruiert wurde Nil, Es hat keine Felder.

Wenn ich tippe "Cons 0 Nil"Es verwendet das"Cons"Value Constructor zum Erstellen einer Instanz der Liste. Aus 0 wird er erfahren, dass der Typparameter ist"Integer". Bisher keine Verwirrung.

Es wird jedoch auch festgestellt, dass der Wert des ersten Feldes des Nachteils 0 ist. Doch nichts über den Wert des zweiten Feldes ermittelt ... es bestimmt nur, dass das zweite Feld eine Art "List Integer" hat.

Nein, es bestimmt, dass der Wert des zweiten Feldes ist Nil. Angesichts Ihrer Definition, Nil ist ein Wert des Typs List a. Deshalb auch Cons 0 Nil. Und in Cons 1 it Der Wert des zweiten Feldes ist it; dh,, Cons 0 Nil. Genau das zeigt die Reply:Cons 1 (Cons 0 Nil).

Ich habe mir Ihre bearbeitete Frage angesehen.

Wenn ich Instanzen mit dem Cons Value Constructor erstelle, "interpretieren diese Instanzen" das erste "a" als Bedeutung "den hier übergebenen Wert.

In "Cons A (Liste A)" sind beide "a" und "list a" Typen. Ich verstehe nicht, was "Wert" damit hat.

Wenn ich "cons 0 nil" eingeben, verwendet es den "Cons" -Wwertkonstruktor, um eine Instanz der Liste zu erstellen. Aus 0 erfährt es, dass der Typparameter "Integer" ist. Bisher keine Verwirrung.

Es wird jedoch auch festgestellt, dass der Wert des ersten Feldes des Nachteils 0 ist. Doch nichts über den Wert des zweiten Feldes ermittelt ... es bestimmt nur, dass das zweite Feld eine Art "List Integer" hat.

Der Wert des zweiten Feldes ist Nil.

Meine Frage ist also, warum bedeutet "a" im ersten Feld "Der Typ dieses Feldes ist 'a' und der Wert dieses Feldes ist 'a'", während "A" im zweiten Feld nur "den Typ bedeutet" Von diesem Feld ist 'Liste eines' '?

"a" im ersten Feld bedeutet "Der Typ dieses Feldes ist 'a'". "List a" im zweiten Feld bedeutet "Der Typ dieses Feldes ist 'Liste a'". Im Fall von "cons 0 nil" oben ist 'a' als "Ganzzahl" abgelehnt. Also wird "cons a (Liste a)" "cons Integer (Liste integer)". Die 0 ist ein Wert des Typ -Ganzzahl. Der NIL ist ein Wert des Typs "List Integer".

Der Wert dieses Feldes ist 'a'

Ich verstehe nicht, was du damit meinst. 'a' ist eine Typvariable; Was hat es mit Werten zu tun?

Nur um Ihnen zusätzliche "Hilfe" zu geben, falls Sie diesen Thread immer noch sehen. Haskell hat einige Konventionen, die die Vorstellungen anderer durcheinander bringen, wie die Dinge getan werden sollten - in Haskell wird ein parametrisierter Typ so häufig akzeptiert, dass es normalerweise als eine Funktion auf Typ -Ebene angesehen wird. Ebenso werden die Wertkonstruktoren als "spezielle" Funktionen betrachtet, die auch Musteranpassungen zusätzlich zu ihrem "Wert nehmen (oder mehr) und dadurch einen Wert erzeugen".

Ein weiteres "lustiges" Merkmal von Haskell ist, dass es die Argumente zu einer Funktion nicht explizit (oder implizit) bewertet. Auch wenn sich dieses Argument in Klammern befindet. Lassen Sie mich das etwas anders sagen: Haskell -Funktionen bewerten Argumente in Klammern nicht vor anderen Argumenten. Argumente werden nur für Gruppierungszwecke in Klammern gestellt, um sie nicht "zuerst" zu bewerten. Haskell weist ("gilt") eine Funktion mit einer höheren Vorrang vor als bei jeder anderen Operation - sogar höher als eine implizite Funktion eines seiner eigenen Argumente. Deshalb das der Grund Cons Der Konstruktor hat Parens rund um das zweite Argument, (List a) - um dem Compiler das zu sagen Cons hat zwei Argumente und nicht drei. Die Klammern sind nur für die Gruppierung und nicht für Vorrang!

Seien Sie als ein Nebenthema mit den Typen in F#vorsichtig. Da F# seine Wurzeln in ML hat, haben seine parametrisierten Typen die Parameter vor - int list, nicht (List Int) hinten! Haskell macht es anders, denn es ist genauso wie Haskell funktioniert - zuerst die Funktion, dann Argumente an die Funktion. Dies fördert ein gemeinsames Gebrauchsmuster und erklärt, warum Haskell -Typen und Wertkonstruktoren aktiviert sind - um Sie daran zu erinnern, dass Sie mit einem Typ/Klassenbezogenen zu tun haben.

Okay ich bin fertig; Vielen Dank, dass ich diesen riesigen Text von Text auf Ihr Eigentum setzen durfte ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top