Typen in Lisp und Schema

10

Ich sehe jetzt, dass Racket Typen hat. Auf den ersten Blick scheint es fast identisch mit der Haskell-Eingabe zu sein. Aber deckt Lisps CLOS einige der Bereiche ab, die Haskell-Typen abdecken? Das Erstellen eines sehr strengen Haskell-Typs und eines Objekts in einer beliebigen OO-Sprache scheint vage ähnlich zu sein. Es ist nur so, dass ich etwas von der Haskell-Kool-Hilfe getrunken habe und ich bin total paranoid, dass ich wegen dynamischer Eingabe geschraubt werde, wenn ich die Lisp-Straße entlang gehe.

user2054900
quelle

Antworten:

6

Das CL-Typsystem ist aussagekräftiger als das Haskell- System. Sie können beispielsweise einen Typ (or (integer 1 10) (integer 20 30))für einen Wert festlegen 1,2,...9,10,20,21,...,30.

Lisp-Compiler erzwingen jedoch nicht das Verständnis der Typensicherheit in Ihrem Hals, sodass Sie ihre "Notizen" ignorieren können - auf eigenes Risiko .

Dies bedeutet, dass Sie Haskell (sozusagen) in Lisp schreiben können, indem Sie alle Werttypen deklarieren und sorgfältig sicherstellen, dass alle erforderlichen Typen abgeleitet werden. Dann ist es jedoch einfacher, Haskell überhaupt zu verwenden.

Wenn Sie eine starke statische Typisierung wünschen, verwenden Sie grundsätzlich Haskell oder OCaml. Wenn Sie eine starke dynamische Typisierung wünschen, verwenden Sie Lisp. Wenn Sie eine schwache statische Typisierung wünschen, verwenden Sie C, wenn Sie eine schwache dynamische Typisierung möchten, verwenden Sie Perl / Python. Jeder Pfad hat seine Vor- (und Eiferer) und Nachteile (und Kritiker), sodass Sie davon profitieren, alle zu lernen.

sds
quelle
19
Jeder, der Begriffe wie "Typensicherheit in den Hals zwingen" verwendet, versteht nicht, was Typensicherheit ist oder warum sie nützlich ist.
Mason Wheeler
11
@MasonWheeler: Jeder, der aus einer einzelnen Phrase eine umfassende Schlussfolgerung zieht, wird öfter als sonst falsch liegen. Wie zum Beispiel in diesem Fall.
SDS
4
Da es sich um Sprachen handelt, ist der Begriff "Down your Throat" angemessen und passend.
Luser Droog
1
@KChaloux: Ich meinte "ausdrucksstark", wie das Beispiel verdeutlicht.
SDS
4
Du hast alles rückwärts. Die dynamische Eingabe ist ein Sonderfall der statischen Eingabe, bei dem Sie den Programmierer zwingen, für alles einen Typ zu verwenden. Ich kann dasselbe (wörtlich) in den meisten statisch typisierten Sprachen tun, indem ich jede Variable als Typ deklariere Objectoder was auch immer die Wurzel des Typbaums ist. Dies ist weniger ausdrucksvoll , weil Sie beraubt die Möglichkeit zu sagen , dass bestimmte Variablen nur bestimmte Werte enthalten.
Doval
5

Typed Racket unterscheidet sich stark von Haskell. Typsysteme in Lisp und Scheme und tatsächlich Typsysteme in traditionell untypisierten Sprachökosystemen im Allgemeinen haben ein grundlegendes Ziel, das andere Typsysteme nicht haben - sie arbeiten mit vorhandenem untypisiertem Code zusammen . Typed Racket führte beispielsweise ganz neue Typisierungsregeln ein, um verschiedene Racket-Redewendungen zu berücksichtigen. Betrachten Sie diese Funktion:

(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

Bei nicht leeren Listen wird das erste Element zurückgegeben. Bei leeren Listen wird false zurückgegeben. Dies ist in untypisierten Sprachen üblich. Eine getippte Sprache würde einen Wrapper-Typ wie verwenden Maybeoder im leeren Fall einen Fehler auslösen. Wenn wir dieser Funktion einen Typ hinzufügen möchten, welcher Typ sollte verwendet werden? Es ist nicht [a] -> a(in Haskell-Notation), weil es false zurückgeben kann. Dies ist auch nicht [a] -> Either a Booleander Fall, da (1) im leeren Fall immer false zurückgegeben wird, kein beliebiger Boolescher Wert, und (2) ein Typ vom Typ Elemente einschließt Leftund falsch Righteinfügt und Sie das "entweder auspacken" müssen, um zum eigentlichen Element zu gelangen. Stattdessen gibt der Wert eine echte Vereinigung zurück- Es gibt keine Wrapping-Konstruktoren, es wird in einigen Fällen einfach ein Typ und in anderen Fällen ein anderer Typ zurückgegeben. In Typed Racket wird dies mit dem Union-Typ-Konstruktor dargestellt:

(: first (All (A) (-> (Listof A) (U A #f))))
(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

Der Typ (U A #f)gibt an, dass die Funktion entweder ein Element der Liste oder false ohne Umbruchinstanz zurückgeben kann Either. Der Typprüfer kann daraus schließen, dass some-listes sich entweder um einen Typ (Pair A (Listof A))oder um eine leere Liste handelt, und schließt daraus, dass in den beiden Zweigen der if-Anweisung bekannt ist, welche davon der Fall ist . Die Typprüfung weiß, dass (car some-list)die Liste im Ausdruck den Typ haben muss , (Pair A (Listof A))da die if-Bedingung dies sicherstellt. Dies wird als Vorkommenstypisierung bezeichnet und soll den Übergang von nicht typisiertem Code zu typisiertem Code erleichtern.

Das Problem ist die Migration. Es gibt eine Menge untypisierten Racket-Codes, und Typed Racket kann Sie nicht einfach dazu zwingen, alle Ihre bevorzugten untypisierten Bibliotheken aufzugeben und einen Monat damit zu verbringen, Ihrer Codebasis Typen hinzuzufügen, wenn Sie ihn verwenden möchten. Dieses Problem tritt immer dann auf, wenn Sie einer vorhandenen Codebasis schrittweise Typen hinzufügen. Eine JavaScript-Anwendung dieser Ideen finden Sie unter TypeScript und unter Beliebiger Typ.

Ein schrittweises Typsystem muss Werkzeuge für den Umgang mit gängigen untypisierten Redewendungen und die Interaktion mit vorhandenem untypisiertem Code bereitstellen. Andernfalls ist die Verwendung sehr schmerzhaft. Ein Beispiel für Clojure finden Sie unter "Warum wir Core.typed nicht mehr verwenden" .

Jack
quelle
1
In der Wirtschaft ist dies als Greshams Gesetz bekannt : Schlechtes Geld vertreibt Gutes. Es findet tendenziell auch Anwendungen im Ingenieurwesen. Wenn Sie zwei theoretisch äquivalente Subsysteme in Ihrem System haben, verursacht das schlechtere allzu oft zu viele Probleme, um das bessere zu verwenden, wie wir hier sehen.
Mason Wheeler
"Ein Beispiel für das Entwerfen eines Typsystems, das": Dieser Satz ist nicht vollständig
Coredump
@MasonWheeler Das Schlimmste ist, lassen Sie mich raten, die dynamische Typprüfung. Sobald Sie es einführen, lohnt es sich nicht, eine statische Analyse durchzuführen?
Coredump
1
@coredump - Die schrittweise Eingabe lohnt sich, es sei denn, Sie interagieren schlecht mit nicht typisiertem Code. Der Typ Any in TypeScript weist den Typechecker beispielsweise nur an, aufzugeben, und wirft im Grunde genommen die Vorteile des Typsystems aus, da dies dazu führen kann, dass sich ein schlechter Wert durch große Mengen statisch überprüften Codes ausbreitet. Typed Racket verwendet Verträge und Modulgrenzen, um dieses Problem zu vermeiden.
Jack
1
@coredump Lesen Sie den Clojure-Artikel. Das dynamische Tippen, insbesondere mit dem gesamten Code von Drittanbietern, für den keine statischen Typen verfügbar waren, hat die Versuche, die Codebasis mit statischen Typen zu verbessern, wirklich durcheinander gebracht.
Mason Wheeler