ZuriHac: funktionale Programmierung üben

Im Juni dieses Jahres fand in der Schweizer Kleinstadt Rapperswil eine Veranstaltung statt ZuriHac. Diesmal brachte es mehr als fünfhundert Haskell-Liebhaber zusammen, vom Anfänger bis zum Gründervater der Sprache. Obwohl die Organisatoren diese Veranstaltung als Hackathon bezeichnen, handelt es sich weder um eine Konferenz noch um einen Hackathon im klassischen Sinne. Sein Format unterscheidet sich von herkömmlichen Programmierern. Durch Zufall haben wir von ZuriHac erfahren, daran teilgenommen und sehen es nun als unsere Pflicht an, von dem ungewöhnlichen Fund zu berichten!

ZuriHac: funktionale Programmierung üben

Über uns

Dieser Artikel wurde von zwei Studenten des dritten Studienjahres des Programms „Angewandte Mathematik und Informatik“ an der National Research University Higher School of Economics – St. Petersburg verfasst: Vasily Alferov und Elizaveta Vasilenko. Die Leidenschaft für funktionale Programmierung begann für uns beide mit einer Vorlesungsreihe von D. N. Moskvin im 3. Studienjahr. Vasily nimmt derzeit am Google Summer of Code-Programm teil, in dessen Rahmen er unter Anleitung des Projektteams algebraische Graphen in Haskell implementiert Alge. Elizaveta wandte die erworbenen Fähigkeiten der funktionalen Programmierung in Kursarbeiten an, die sich der Implementierung des Anti-Vereinigungs-Algorithmus mit anschließender Anwendung in der Typentheorie widmeten.

Veranstaltungsformat

Die Zielgruppe sind Eigentümer von Open-Source-Projekten, Programmierer, die an ihrer Entwicklung teilnehmen möchten, Forscher im Bereich der funktionalen Programmierung und Menschen, die sich einfach für Haskell begeistern. Dieses Jahr trafen sich Entwickler von mehr als fünfzig Open-Source-Haskell-Projekten aus aller Welt am Veranstaltungsort – der HSR Hochschule für Technik Rapperswil –, um über ihre Produkte zu sprechen und neue Leute für ihre Entwicklung zu interessieren.

ZuriHac: funktionale Programmierung üben

Foto von Twitter ZuriHac

Das Schema ist ganz einfach: Sie müssen im Voraus einige Vorschläge zu Ihrem Projekt verfassen und diese an die Organisatoren senden, die Informationen zu Ihrem Projekt auf der Veranstaltungsseite veröffentlichen. Darüber hinaus haben die Autoren der Projekte am ersten Tag dreißig Sekunden Zeit, um von der Bühne aus ganz kurz zu erzählen, was sie tun und was getan werden muss. Dann suchen Interessierte nach den Autoren und fragen ausführlich nach den Aufgaben.

Wir haben noch keine eigenen offenen Projekte, aber wir möchten unbedingt zu bestehenden beitragen, deshalb haben wir uns als reguläre Teilnehmer registriert. Drei Tage lang haben wir mit zwei Entwicklergruppen zusammengearbeitet. Es stellt sich heraus, dass das gemeinsame Studium von Code und Live-Kommunikation die Interaktion zwischen Projektautoren und Mitwirkenden sehr produktiv macht – bei ZuriHac konnten wir Bereiche verstehen, die für uns neu waren, und konnten zwei völlig unterschiedlichen Teams helfen, indem wir jeweils eine Aufgabe erledigten der Projekte.

Neben wertvoller Praxis wurden im ZuriHac auch mehrere Vorlesungen und Meisterkurse gehalten. Zwei Vorträge sind uns besonders in Erinnerung geblieben. Im ersten Vortrag sprach Andrey Mokhov von der University of Newcastle über selektive applikative Funktoren – eine Klasse von Typen, die zwischen applikativen Funktoren und Monaden liegen sollten. In einem anderen Vortrag sprach einer der Gründer von Haskell, Simon Peyton Jones, darüber, wie Typinferenz im GHC-Compiler funktioniert.

ZuriHac: funktionale Programmierung üben

Vortrag von Simon Peyton Jones. Foto von Twitter ZuriHac

Die im Rahmen des Hackathons durchgeführten Meisterkurse wurden je nach Ausbildungsstand der Teilnehmer in drei Kategorien eingeteilt. Auch die den Teilnehmern, die sich an der Entwicklung von Projekten beteiligten, angebotenen Aufgaben wurden mit einem Schwierigkeitsgrad gekennzeichnet. Die kleine, aber freundliche Gemeinschaft funktionaler Programmierer heißt Neulinge gerne in ihren Reihen willkommen. Um die Vorlesungen von Andrei Mokhov und Simon Peyton Jones zu verstehen, war der Kurs über funktionale Programmierung, den wir an der Universität belegten, jedoch sehr nützlich.

Die Anmeldung zur Veranstaltung ist sowohl für reguläre Teilnehmer als auch für Projektautoren kostenlos. Wir reichten Anfang Juni Bewerbungen für die Teilnahme ein, woraufhin wir schnell von der Warteliste auf die Liste der bestätigten Teilnehmer übertragen wurden.

Und jetzt werden wir über die Projekte sprechen, an deren Entwicklung wir beteiligt waren.

Pandak

Pandak ist ein universeller Konverter von Textdokumenten, und zwar von jedem Format in jedes. Zum Beispiel von docx zu pdf oder von Markdown zu MediaWiki. Sein Autor, John MacFarlane, ist Professor für Philosophie an der University of California in Berkeley. Im Allgemeinen ist Pandoc ziemlich berühmt und einige unserer Freunde waren überrascht, als sie erfuhren, dass Pandoc in Haskell geschrieben wurde.

ZuriHac: funktionale Programmierung üben

Liste der von Pandoc unterstützten Dokumentformate. Es gibt auch eine ganze Grafik auf der Seite, aber dieses Bild passt nicht in den Artikel.

Natürlich bietet Pandoc nicht für jedes Formatpaar eine direkte Konvertierung an. Um eine so große Vielfalt an Transformationen zu unterstützen, wird eine Standardarchitekturlösung verwendet: Zuerst wird das gesamte Dokument in eine spezielle interne Zwischendarstellung übersetzt und dann wird aus dieser internen Darstellung ein Dokument in einem anderen Format generiert. Die Entwickler nennen die interne Darstellung „AST“, was für Abstract Syntax Tree steht, bzw. „Abstract Syntax Tree“. abstrakter Syntaxbaum. Die Zwischendarstellung können Sie ganz einfach betrachten: Sie müssen lediglich das Ausgabeformat auf „nativ“ setzen.

$ cat example.html
<h1>Hello, World!</h1>

$ pandoc -f html -t native example.html
[Header 1 ("hello-world",[],[]) [Str "Hello,",Space,Str "World!"]]

Leser, die zumindest ein wenig mit Haskell gearbeitet haben, können anhand dieses kleinen Beispiels bereits vermuten, dass Pandoc in Haskell geschrieben ist: Die Ausgabe dieses Befehls ist eine String-Darstellung der internen Strukturen von Pandoc, erstellt in der Art, wie es normalerweise gemacht wird in Haskell. Zum Beispiel in der Standardbibliothek.

Hier sehen Sie also, dass die interne Darstellung eine rekursive Struktur ist, in der sich in jedem internen Knoten eine Liste befindet. Auf der obersten Ebene gibt es beispielsweise eine Liste mit einem Element – ​​dem Header der ersten Ebene mit den Attributen „hello-world“,[],[]. In diesem Header verbirgt sich eine Liste der Zeichenfolge „Hello“, gefolgt von einem Leerzeichen und der Zeichenfolge „World!“.

Wie Sie sehen, unterscheidet sich die interne Darstellung nicht wesentlich von HTML. Es handelt sich um einen Baum, in dem jeder interne Knoten einige Informationen über die Formatierung seiner Nachkommen bereitstellt und die Blätter den tatsächlichen Inhalt des Dokuments enthalten.

Wenn wir zur Implementierungsebene gehen, wird der Datentyp für das gesamte Dokument wie folgt definiert:

data Pandoc = Pandoc Meta [Block]

Hier handelt es sich beim Block genau um die oben genannten internen Eckpunkte, und bei Meta handelt es sich um Metainformationen über das Dokument, wie Titel, Erstellungsdatum, Autoren – dies ist für verschiedene Formate unterschiedlich, und Pandoc versucht, solche Informationen bei der Übersetzung von Format in nach Möglichkeit beizubehalten Format.

Fast alle Konstruktoren vom Typ Block – zum Beispiel Header oder Para (Absatz) – nehmen Attribute und eine Liste von Vertices niedrigerer Ebene als Argumente – in der Regel Inline. Space oder Str sind beispielsweise Konstruktoren vom Typ Inline, und auch das HTML-Tag wird zu seinem eigenen speziellen Inline. Wir sehen keinen Sinn darin, eine vollständige Definition dieser Typen bereitzustellen, beachten Sie jedoch, dass diese hier zu finden ist hier.

Interessanterweise ist der Typ Pandoc ein Monoid. Das bedeutet, dass es sich um eine Art leeres Dokument handelt und dass Dokumente gestapelt werden können. Dies ist praktisch beim Schreiben von Readern – Sie können ein Dokument mithilfe willkürlicher Logik in Teile aufteilen, jeden einzeln analysieren und dann alles in einem Dokument zusammenfügen. In diesem Fall werden Metainformationen von allen Teilen des Dokuments gleichzeitig erfasst.

Bei der Konvertierung beispielsweise von LaTeX nach HTML konvertiert zunächst ein spezielles Modul namens LaTeXReader das Eingabedokument in AST, dann konvertiert ein anderes Modul namens HTMLWriter das AST in HTML. Dank dieser Architektur ist es nicht erforderlich, eine quadratische Anzahl von Konvertierungen zu schreiben – es reicht aus, Reader und Writer für jedes neue Format zu schreiben, und alle möglichen Konvertierungspaare werden automatisch unterstützt.

Es ist klar, dass eine solche Architektur auch Nachteile hat, die von Experten auf dem Gebiet der Softwarearchitektur schon lange vorhergesagt wurden. Am bedeutendsten sind die Kosten für Änderungen am Syntaxbaum. Wenn die Änderung schwerwiegend genug ist, müssen Sie den Code in allen Readern und Writern ändern. Eine der Herausforderungen für Pandoc-Entwickler ist beispielsweise die Unterstützung komplexer Tabellenformate. Jetzt kann Pandoc nur noch sehr einfache Tabellen erstellen, mit einer Überschrift, Spalten und einem Wert in jeder Zelle. Beispielsweise wird das colspan-Attribut in HTML einfach ignoriert. Einer der Gründe für dieses Verhalten ist das Fehlen eines einheitlichen Schemas zur Darstellung von Tabellen in allen oder zumindest vielen Formaten – dementsprechend ist unklar, in welcher Form die Tabellen in der internen Darstellung gespeichert werden sollen. Aber auch nach der Auswahl einer bestimmten Ansicht müssen Sie unbedingt alle Reader und Writer ändern, die die Arbeit mit Tabellen unterstützen.

Die Wahl der Sprache Haskell fiel nicht nur auf die große Liebe der Autoren zur funktionalen Programmierung. Haskell ist für seine umfangreichen Textverarbeitungsfunktionen bekannt. Ein Beispiel ist die Bibliothek Parsec ist eine Bibliothek, die die Konzepte der funktionalen Programmierung – Monoide, Monaden, applikative und alternative Funktoren – aktiv nutzt, um beliebige Parser zu schreiben. Die volle Leistungsfähigkeit von Parsec zeigt sich in Beispiel aus HaskellWiki, wo ein vollständiger Parser einer einfachen imperativen Programmiersprache analysiert wird. Natürlich wird Parsec auch in Pandoc aktiv eingesetzt.

Kurz gesagt: Monaden werden zum sequentiellen Parsen verwendet, wenn eines zuerst kommt und dann das andere. In diesem Beispiel zum Beispiel:

whileParser :: Parser Stmt
whileParser = whiteSpace >> statement

Zuerst müssen Sie das Leerzeichen zählen und dann die Anweisung, die ebenfalls vom Typ Parser Stmt ist.

Alternative Funktoren werden zum Rollback verwendet, wenn das Parsen fehlschlägt. Zum Beispiel,

statement :: Parser Stmt
statement = parens statement <|> sequenceOfStmt

Das bedeutet, dass Sie entweder versuchen müssen, die Anweisung in Klammern zu lesen, oder dass Sie versuchen müssen, mehrere Anweisungen nacheinander zu lesen.

Applikative Funktoren werden hauptsächlich als Abkürzungen für Monaden verwendet. Lassen Sie beispielsweise die tok-Funktion ein Token lesen (dies ist eine echte Funktion von LaTeXReader). Schauen wir uns diese Kombination an

const <$> tok <*> tok

Es liest zwei Token hintereinander und gibt das erste zurück.

Für alle diese Klassen verfügt Haskell über wunderschöne symbolische Operatoren, die die Reader-Programmierung wie ASCII-Kunst aussehen lassen. Bewundern Sie einfach diesen wunderbaren Code.

Unsere Aufgaben bezogen sich auf LaTeXReader. Vasilys Aufgabe bestand darin, die Befehle mbox und hbox zu unterstützen, die zum Schreiben von Paketen in LaTeX nützlich sind. Elizabeth war für die Unterstützung des Epigraph-Befehls verantwortlich, mit dem Sie Epigraphen in LaTeX-Dokumenten erstellen können.

Hatrace

UNIX-ähnliche Betriebssysteme implementieren häufig den Systemaufruf ptrace. Es ist nützlich beim Debuggen und Simulieren von Programmumgebungen und ermöglicht es Ihnen, die Systemaufrufe des Programms zu verfolgen. Beispielsweise verwendet das sehr nützliche Dienstprogramm Strace intern Ptrace.

Hatrace ist eine Bibliothek, die eine Schnittstelle zu ptrace in Haskell bereitstellt. Tatsache ist, dass ptrace selbst sehr anspruchsvoll ist und es ziemlich schwierig ist, es direkt zu verwenden, insbesondere aus funktionalen Sprachen.

Hatrace läuft beim Start wie Strace und akzeptiert ähnliche Argumente. Sie unterscheidet sich von strace dadurch, dass es sich ebenfalls um eine Bibliothek handelt, die eine einfachere Schnittstelle als nur ptrace bietet.

Mit Hilfe von Hatrace haben wir bereits einen unangenehmen Fehler im GHC Haskell-Compiler entdeckt: Wenn er im falschen Moment beendet wird, generiert er falsche Objektdateien und kompiliert sie beim Neustart nicht neu. Skripting durch Systemaufrufe ermöglichte es, den Fehler zuverlässig in einem Durchlauf zu reproduzieren, während zufällige Kills den Fehler in etwa zwei Stunden reproduzierten.

Wir haben der Bibliothek Systemaufrufschnittstellen hinzugefügt – Elizaveta fügte brk hinzu und Vasily fügte mmap hinzu. Basierend auf den Ergebnissen unserer Arbeit ist es möglich, die Argumente dieser Systemaufrufe bei der Verwendung der Bibliothek einfacher und genauer zu verwenden.

Source: habr.com

Kommentar hinzufügen