Ich weiß, dass dies eine sehr breite, mehrdeutige und möglicherweise philosophische Frage ist. Bis zu einem gewissen Grad ist das wichtigste Schlüsselwort in der Frage - das "starke" Typensystem - selbst - schlecht definiert . Lassen Sie mich erklären, was ich meine.
Gesamtzusammenhang für die Frage
Wir haben eine sehr umfangreiche Web-App in Ruby on Rails erstellt und waren im Allgemeinen sehr zufrieden mit unserem Stack. Wenn wir wollen, können wir wirklich schnell etwas versenden - etwas, das für 90% des "Business" -Falls funktioniert, ohne dass wir uns über 10% Edge-Fälle Gedanken machen müssen. Auf der anderen Seite können wir mit Hilfe von Code-Reviews und Test-Coverage langsam und überlegt vorgehen und sicherstellen, dass wir alle Grundlagen abdecken - wiederum nur in Situationen, die eine genauere Prüfung und Sicherheit verdienen.
Mit dem Anwachsen des Teams wurde es mir jedoch unangenehm, dass kein "Sicherheitsnetz" direkt in unseren Stapel gebacken wurde.
Wir haben vor kurzem damit begonnen, native Android-Entwicklung auf Java durchzuführen. Und ich wurde (angenehm) an die Sicherheit erinnert, die eine kompilierte / statische / stark typisierte Sprache bietet.
- Falsch geschriebene Variablen, falsche Datentypen, falsche Funktionsaufrufe und eine Vielzahl trivialer Fehler werden von Ihrer IDE selbst abgefangen. Alles, weil die IDE sich in den Compiler einbinden und bestimmte Aspekte der Programmkorrektheit überprüfen kann.
- Müssen Sie eine Funktionssignatur ändern? Einfach. Die Compiler + IDE kann Ihnen helfen, ALLE Aufrufstellen zu erkennen.
- Müssen Sie sicherstellen, dass bestimmte Ausnahmen immer behandelt werden? Überprüfte Ausnahmen zu Ihrer Rettung.
Diese Sicherheitsmerkmale haben zwar ihre Vorteile, aber ich bin mir auch ihrer Nachteile bewusst. Mehr noch, in der Welt von "Boilerplate Heavy" Java. Aus diesem Grund habe ich mich anstelle von Java mit den modernen, stark typisierten Sprachen befasst, an denen die Menschen in diesen Tagen zu arbeiten begonnen haben. Zum Beispiel: Scala, Rust, Haskell usw. Was mich am meisten interessiert, ist die Leistungsfähigkeit ihrer Typsysteme und die Überprüfung der Statik / Kompilierzeit.
Nun die Frage
Wie verwende ich diese leistungsstarken Typsysteme und Funktionen zur statischen / Kompilierungszeit in größeren Anwendungen?
Wie würde ich mich zum Beispiel jenseits der üblichen "Hallo Welt" -Einführungen in diese leistungsstarken Funktionen bewegen? Ein System, das ein Rich-Type-System zur Modellierung eines Business-Domain-Problems verwendet? Hilft oder behindert das Typsystem, wenn Sie sich in der Zone mit 30.000 LOC + befinden? Was passiert mit dem von diesen Typsystemen bereitgestellten Sicherheitsnetz (und Überprüfungen der Kompilierungszeit), wenn Ihr System mit der schwach typisierten Außenwelt interagiert, z. über JSON- oder XML-APIs, verschiedene Datenspeicher, Benutzereingaben usw.
Antworten:
Ich werde aus Zeitgründen eine kurze Antwort geben, arbeite aber derzeit an zwei großen Projekten (> 100.000 LOC in Haskell) - flowbox.io und luna-lang.org. Wir verwenden Haskell für alle Teile, einschließlich des Backends, des Compilers unserer Programmiersprache und sogar der webGL-basierten GUI. Ich muss zugeben, dass das starke Typensystem und die "abhängige Art" -ähnliche Maschinerie Sie führen und Sie vor Belastungen und Schwierigkeiten bewahren können, die aus anderen Sprachen bekannt sind. Wir verwenden die Typen sehr ausgiebig und alles, was in der Kompilierzeit überprüft werden konnte, wird so gemacht. Tatsächlich haben wir in den letzten 3 Jahren der Entwicklung nie einen Laufzeitfehler oder einen Stapelüberlauf festgestellt (und das ist wirklich unglaublich). Die einzigen Fehler sind offensichtliche Logikfehler, die von Programmierern gemacht werden. Viele Leute sagen, dass, wenn etwas in Haskell kompiliert wird, es einfach funktioniert und Sie ziemlich sicher sein sollten, dass es eines Tages nicht in Ihr Gesicht bläst.
Beantwortung des ersten Teils der Frage: Sie können sich mit diesen leistungsstarken Typsystemfunktionen vertraut machen, indem Sie einige großartige Blogs lesen, z.
Tatsächlich gibt es viele andere nette Blogs da draußen (wie Planet Haskell ). Wie auch immer, die beste Methode, um die fortschrittlichen Typsysteme wirklich zu verstehen, ist die Entwicklung einer nützlichen Open-Source-Bibliothek. Wir (bei Flowbox & New Byte Order) veröffentlichen viele Bibliotheken (Sie finden sie bei Hackage). Wenn Sie also keine Ahnung haben, was Sie entwickeln sollen, können Sie sich jederzeit an unseren Projekten beteiligen - schicken Sie mir einfach eine E-Mail, wann immer Sie möchten wollen (Mail verfügbar bei luna-lang.org ).
quelle
Nun, schwaches vs. starkes Tippen ist ziemlich vage definiert. Da der allgemeine Gebrauch von "starker Typisierung" am ehesten darin besteht, Dinge zu bezeichnen, die das Gießen von Typen erschweren, bleibt nichts anderes übrig, um noch stärkere Typensysteme zu beschreiben. Es ist wie zu sagen, wenn Sie weniger als 30 Pfund tragen können, sind Sie schwach, und jeder, der mehr heben kann, ist in der gleichen Kategorie von "stark" - eine irreführende Unterscheidung.
Also bevorzuge ich die Definition:
Was meine ich damit, Dinge für dich zu tun? Nun, lassen Sie uns das Schreiben einer Bildkonvertierungs-API im Servant-Framework untersuchen (in Haskell, aber Sie müssen es nicht wirklich wissen, um mitzumachen, Sie werden sehen ...)
Dies bedeutet, dass wir einige Module, einschließlich des Servant-Pakets und des JuicyPixels-Plugins für Servant, benötigen und dass der Haupteinstiegspunkt des Programms darin besteht, die Konvertierungsfunktion auf Port 8001 als Server über das Warp-Backend auszuführen. Ignoriere das Sprachbit.
Dies bedeutet, dass die Konvertierungsfunktion ein Server ist, auf dem die API dem Typ 'ConversionApi' entsprechen muss und die Anforderungen von der Funktion verarbeitet werden
handler
Dies gibt den
ConvesionApi
Typ an. Es heißt, wir sollten eingehende Inhaltstypen akzeptieren, die in der Liste '[BMP, GIF, JPEG 50, PNG, TIFF, RADIANCE] angegeben sind, und sie als DynamicImage behandeln und ein DynamicImage zurückgeben, das in denselben Inhaltsbereich konvertiert wurde Typen. Machen Sie sich keine Gedanken darüber, was:> bedeutet, stellen Sie es sich vorerst als glückliche Magie vor.Aufgrund meiner bevorzugten Definition kann ein schwach typisiertes System nun Folgendes sicherstellen:
Alle hohen Ziele, aber nicht genug, um sich als stark typisiertes System zu qualifizieren, wenn man die obige Definition zugrunde legt. Und jetzt müssen wir uns dem schwierigen Teil des eigentlichen Schreibens von Code zuwenden, der dieser Spezifikation folgt. In einem wirklich starken Typensystem schreiben wir:
Und dann sind wir fertig. Das war's, es gibt keinen Code mehr zum Schreiben . Dies ist ein voll funktionsfähiger Webserver (Modulo alle Tippfehler, die ich verpasst habe). Der Typ hat dem Compiler alles mitgeteilt, was er zum Erstellen unseres Webservers aus den von uns definierten und importierten Typen und Paketen (Module technisch gesehen) benötigt.
Wie lernen Sie dies auf der Hauptanwendungsskala? Nun, es unterscheidet sich nicht wesentlich von der Verwendung in kleineren Anwendungen. Absolute Typen interessieren sich nicht dafür, wie viel Code in Bezug auf sie geschrieben ist.
Die Typprüfung zur Laufzeit ist etwas, das Sie wahrscheinlich vermeiden möchten, da dies einen großen Teil des Nutzens einspart und es den Typen ermöglicht, die Arbeit mit Ihrem Projekt komplizierter zu gestalten, anstatt die Dinge durch Typen zu vereinfachen.
Daher ist es meist nur eine Frage der Übung, Dinge mit Typen zu modellieren. Die beiden Hauptmethoden zum Modellieren von Dingen (oder zum Erstellen von Dingen im Allgemeinen) sind Bottom-up und Top-down. Top-down beginnt mit der höchsten Ebene von Operationen, und wenn Sie das Modell erstellen, haben Sie Teile, bei denen Sie die Modellierung auf einen späteren Zeitpunkt verschieben. Bottom-up-Modellierung bedeutet, dass Sie mit Basisoperationen beginnen, genau wie Sie mit Basisfunktionen beginnen, und dann immer größere Modelle erstellen, bis Sie die Operation des Projekts vollständig erfasst haben. Bottom-up ist konkreter und lässt sich wahrscheinlich schneller erstellen, aber Top-down kann Ihre Modelle auf niedrigerer Ebene besser darüber informieren, wie sie sich tatsächlich verhalten müssen.
Typen beziehen sich im wahrsten Sinne des Wortes auf Mathematik. Es gibt also keine Obergrenze dafür, wie kompliziert sie werden können, oder einen Punkt, an dem man "fertig" sein kann, um etwas über sie zu lernen. Nahezu alle Ressourcen außerhalb von Universitätskursen auf höherer Ebene beziehen sich auf die Funktionsweise von Typen in einer bestimmten Sprache. Sie müssen sich also auch dafür entscheiden.
So gut ich kann, können Typen wie folgt geschichtet werden:
Je weiter unten auf dieser Liste Sie sich befinden, desto mehr können Typen für Sie tun, aber ganz unten steigen Sie in die Stratosphäre auf und die Luft wird etwas dünn - das Paket-Ökosystem ist viel kleiner und Sie ' Ich muss mehr Dinge selbst schreiben, als eine relevante Bibliothek gefunden zu haben. Die Eintrittsbarriere steigt auch mit dem Abstieg, da Sie das Typensystem so gut verstehen müssen, dass Sie umfangreiche Programme schreiben können.
quelle
Proxy :: Proxy True
funktioniert, aber es ist besser, es so zu schreibenProxy :: Proxy 'True
.'[xs]
Syntax kurz erläutern ? Dies ist eindeutig keineChar
, aber ich verstehe weder, wieTypeOperators
oder wieDataKinds
eine alternative Syntax aktiviert wird. Ist es eine Art Quasiquotierung?Ich habe gerade angefangen, im Kernteam einer großen Plattform zu arbeiten, die in Scala geschrieben wurde. Sie können sich erfolgreiche Open Source-Anwendungen wie Scalatra, Play oder Slick ansehen, um zu sehen, wie diese mit einigen Ihrer detaillierteren Fragen zur Interaktion mit dynamischen Datenformaten umgehen.
Einer der großen Vorteile der starken Typisierung von Scala liegt in der Benutzererziehung. Das Kernteam kann Entscheidungen treffen und diese Entscheidungen im Typensystem durchsetzen. Wenn also andere Teams, die mit den Entwurfsprinzipien viel weniger vertraut sind, mit dem System interagieren müssen, korrigiert der Compiler sie und das Kernteam korrigiert die Dinge nicht ständig in Anfragen ziehen. Dies ist ein großer Vorteil in einem großen System.
Natürlich können nicht alle Entwurfsprinzipien in einem Typsystem durchgesetzt werden. Je stärker Ihr Typsystem ist, desto mehr Entwurfsprinzipien können Sie im Compiler durchsetzen.
Wir können es den Nutzern auch leichter machen. Oft arbeiten sie nur mit regulären Sammlungen oder Fallklassen, und wir konvertieren sie nach Bedarf für den Netzwerktransport automatisch in JSON oder was auch immer.
Starke Typisierung hilft auch bei der Unterscheidung zwischen nicht bereinigten und bereinigten Eingaben, was die Sicherheit verbessern kann.
Starkes Tippen hilft auch, dass sich Ihre Tests mehr auf Ihr tatsächliches Verhalten konzentrieren , anstatt eine Reihe von Tests zu benötigen, die nur Ihre Typen testen. Es macht das Testen viel angenehmer, fokussierter und damit effektiver.
Der Hauptnachteil ist die Unkenntnis der Sprache und des Sprachparadigmas, und das kann mit der Zeit korrigiert werden. Ansonsten haben wir festgestellt, dass es sich gelohnt hat.
quelle
Obwohl dies keine direkte Antwort ist (da ich noch nicht an +30.000 LOC-Code-Datenbanken in Haskell gearbeitet habe :( ..), bitte ich Sie, https://www.fpcomplete.com/business/resources/case-studies zu lesen / die eine Menge von Fallstudien von haskell in aktuellen Branchenumgebungen enthält.
Ein weiterer guter Artikel ist IMVU, der beschreibt, welche Erfahrungen sie mit der Umstellung auf haskell gemacht haben - http://engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell/ .
Aus persönlicher Erfahrung in größeren Anwendungen hilft Ihnen das Typensystem sehr , insbesondere wenn Sie versuchen, so viel wie möglich in Typen zu codieren. Die wahre Kraft ist wirklich offensichtlich, wenn es darum geht, Dinge umzugestalten - was bedeutet, dass Wartung und dergleichen eine viel weniger beunruhigende Aufgabe werden.
Ich werde ein paar Links zu Ressourcen ausgeben, die ich empfehle, da Sie ziemlich viele Fragen gleichzeitig stellen:
Abschließend sei angemerkt, dass der Umgang mit der Außenwelt auf verschiedene Weise erfolgt. Es gibt Bibliotheken wie Aeson für JSON, Esqueleto für SQL und viele mehr , die sicherstellen, dass Ihre Dokumente typensicher sind.
quelle
Was ich gesehen habe:
Ich habe einige große Ruby-Webanwendungen (Rails), eine große Haskell-Webanwendung und mehrere kleinere gearbeitet. Mit dieser Erfahrung muss ich sagen, dass die Arbeit an den Haskell-Anwendungen in Bezug auf Wartung und geringere Lernkurve viel einfacher ist als bei Rails. Ich bin der Meinung, dass diese Vorteile sowohl auf das Typensystem von Haskell als auch auf den funktionalen Programmierstil zurückzuführen sind. Im Gegensatz zu vielen anderen bin ich jedoch der Meinung, dass der "statische" Teil des Typensystems nur insofern eine große Bequemlichkeit darstellt, als es noch Vorteile bei der Verwendung dynamischer Verträge gibt.
Was ich glaube
Es gibt ein nettes Paket mit dem Namen Contracts Ruby, das einige der Hauptfunktionen bereitstellt, die meiner Meinung nach dazu beitragen, dass Haskell-Projekte bessere Wartungseigenschaften erzielen. Verträge Ruby führt seine Überprüfungen zur Laufzeit durch, sodass es in Kombination mit hoher Testkonvergenz am besten ist. Es bietet jedoch immer noch die gleiche Inline-Dokumentation und Aussagekraft wie die Verwendung von Typanmerkungen in Sprachen wie Haskell.
Anwser zur Frage
Um die oben gestellten Fragen zu beantworten, gibt es viele Stellen, an denen man sich mit Haskell und anderen Sprachen mit fortschrittlichen Typsystemen vertraut machen kann. Um ganz ehrlich zu sein, diese Dokumentationsquellen sind zwar für sich genommen hervorragend, aber im Vergleich zu der Fülle an Dokumentationen und praktischen Ratschlägen, die in Ruby, Python, Java und anderen solchen Sprachen zu finden sind, scheinen sie alle ein wenig überwältigend. In jedem Fall wird Real World Haskell alt, ist aber immer noch eine gute Ressource.
Kategorietheorie
Wenn Sie sich für Haskell entscheiden, werden Sie auf große Mengen an Literatur stoßen, die sich mit Kategorietheorie befasst. IMHO Kategorietheorie ist nützlich, aber nicht notwendig. Angesichts der Verbreitung in der Haskell-Community ist es leicht, die Vor- und Nachteile von Typen mit Gefühlen über die Praktikabilität der Kategorietheorie zu verbinden. Es ist hilfreich, sich daran zu erinnern, dass es sich um zwei verschiedene Dinge handelt, das heißt, Implementierungen, die von der Kategorietheorie geleitet werden, können sowohl in dynamisch typisierten als auch in statischen Sprachen durchgeführt werden (modulo die Vorteile, die das Typensystem bietet). Fortgeschrittene Typensysteme sind im Allgemeinen nicht an die Kategorietheorie und die Kategorietheorie nicht an Typensysteme gebunden.
Mehr zu Typen
Wenn Sie mehr über das Programmieren mit Typen und die darin enthaltenen Techniken erfahren (was aufgrund des Spaßes recht schnell geschieht), möchten Sie mehr mit dem Typensystem ausdrücken. In diesem Fall schaue ich mir einige der folgenden Ressourcen an und mache die Werkzeuganbieter darauf aufmerksam, dass wir Werkzeuge in Industriequalität mit diesen Funktionen nur in einem Paket mit einer benutzerfreundlichen Oberfläche (wie Contracts Ruby) benötigen:
Einführung in die Programmierung in ATS
LiquidHaskell-Verfeinerungstypen über SMT und Predicate Abstraction
Typgetriebene Entwicklung mit Idris
quelle
Erstens habe ich das Gefühl, dass in den Antworten eine Verwechslung zwischen schwach und stark getippt und statisch und dynamisch getippt besteht. Link das OP zur Verfügung gestellt macht deutlich die Unterscheidung:
Beispielsweise sind C, C ++ und Java statisch typisiert, da Variablen zur Kompilierungszeit typisiert werden. C und C ++ können jedoch als schwach typisiert betrachtet werden, da die Sprache die Umgehung von Einschränkungen mithilfe von
void *
Zeigern und Umsetzungen ermöglicht. Mehr zu diesem Thema.Bei dieser Unterscheidung kann eine starke Typisierung nur besser sein. Je früher das Scheitern, desto besser.
Ich glaube jedoch nicht, dass beim Schreiben großer Programme das Typensystem eine wichtige Rolle spielt. Der Linux-Kernel besteht aus zehn Millionen LOC, die in C und Assembly geschrieben wurden. Er gilt als sehr stabiles Programm und ist kilometerweit von meinen 200 Java-Zeilen entfernt, die wahrscheinlich voller Sicherheitslücken sind. Auch wenn dynamisch getippte "Skriptsprachen" beim Schreiben großer Programme einen schlechten Ruf haben, gibt es gelegentlich Beweise dafür, dass sie unverdient sind (wie Python Django, über 70.000 LOC).
Meiner Meinung nach dreht sich alles um den Qualitätsstandard. Die Verantwortung für die Skalierbarkeit großer Anwendungen sollte nur von den Programmierern und Architekten und deren Willen übernommen werden, die Anwendung sauber, getestet, gut dokumentiert usw. zu machen.
quelle
Aus der vorherigen Antwort https://www.fpcomplete.com/business/resources/case-studies/
Es ist wirklich wie jede andere Sprache, um Kraft aufzubauen
Mit abstrakten Datentypen oder allgemeiner mit Polymorphismus
Es hilft den ganzen Weg. Das Typensystem hilft Ihnen beim Schreiben des Codes, da es Ihnen die Form des gewünschten Ergebnisses angibt. Eigentlich schreibt Agda Code für dich.
PS: Machen Sie nicht den Fehler, ein Typensystem zu haben und die Typen selbst schreiben zu müssen, was idiotisch ist: Computer können es für Sie tun.
Das ist großartig, denn wenn Sie die Struktur von Werten über Typen kennen, kann der Computer einen Weg finden, Serializer für Sie zu schreiben (de).
Wenn Sie wirklich etwas über Typen und Abstraktion wissen möchten, finden Sie die beste Einführung in das Verständnis von Typen, Datenabstraktion und Polymorphismus
Es ist ein Papier, keine Fallstudie mit schönen Bildern, aber es ist aufschlussreich
quelle
Als .NET-Programmierer, der viel an Webanwendungen arbeitet, sehe ich sowohl die getippte C # - als auch die untypisierte Javascript-Seite der Dinge.
Ich bin mir nicht sicher, ob ich Literatur zu den von Ihnen gestellten Fragen gesehen habe. Mit einer getippten Sprache nehmen Sie all diese Dinge als selbstverständlich an. Bei einem Typ ohne Typ sehen Sie, wie Sie die Typen als unnötigen Overhead definieren.
Persönlich kann man meiner Meinung nach nicht leugnen, dass eine stark typisierte Sprache die von Ihnen beschriebenen Vorteile zu sehr geringen Kosten bietet (im Vergleich zum Schreiben gleichwertiger Komponententests). Die Interaktion mit schwach typisierten Systemen umfasst normalerweise generische Typen wie Arrays oder Wörterbücher von Objekten wie DataReader oder die kreative Verwendung von Zeichenfolgen oder der neuen dynamischen Klasse. Im Wesentlichen funktioniert alles, Sie erhalten nur einen Laufzeitfehler anstelle der Kompilierungszeit.
Wenn Sie ein sehr kurzes Programm schreiben möchten, beispielsweise eine Funktion in ein paar Zeilen definieren, um mit einer größeren Anwendung zu arbeiten, haben Sie einfach keinen Platz, um Klassen zu deklarieren. Sicherlich ist dies die Nische, die untypisierte Sprachen wie JS besetzen?
quelle