ZuriHac: oefenen met functioneel programmeren

In juni van dit jaar werd in het kleine Zwitserse stadje Rapperswil een evenement gehouden ZuriHac. Deze keer bracht het meer dan vijfhonderd Haskell-liefhebbers samen, van beginners tot de grondleggers van de taal. Hoewel de organisatoren dit evenement een hackathon noemen, is het geen conferentie of hackathon in de klassieke zin van het woord. Het formaat is anders dan bij traditionele programmeurs. We leerden bij toeval over ZuriHac, namen eraan deel en nu beschouwen we het als onze plicht om over de ongewone vondst te vertellen!

ZuriHac: oefenen met functioneel programmeren

Wie zijn wij?

Dit artikel is opgesteld door twee derdejaars studenten van het programma “Toegepaste Wiskunde en Informatica” aan de National Research University Higher School of Economics - St. Petersburg: Vasily Alferov en Elizaveta Vasilenko. De passie voor functioneel programmeren begon voor ons beiden met een serie lezingen door D.N. Moskvin in het tweede jaar van de universiteit. Vasily neemt momenteel deel aan het Google Summer of Code-programma, waarbinnen hij onder begeleiding van het projectteam algebraïsche grafieken in Haskell implementeert alge. Elizaveta paste de verworven functionele programmeervaardigheden toe in cursuswerk gewijd aan de implementatie van het anti-unificatie-algoritme, met daaropvolgende toepassing in de typetheorie.

Evenement formaat

De doelgroep bestaat uit eigenaren van open source-projecten, programmeurs die willen deelnemen aan de ontwikkeling ervan, onderzoekers op het gebied van functioneel programmeren en mensen die simpelweg gepassioneerd zijn door Haskell. Dit jaar kwamen ontwikkelaars van meer dan vijftig open source Haskell-projecten van over de hele wereld bijeen op de locatie - de HSR Hochschule für Technik Rapperswil - om over hun producten te praten en nieuwe mensen te interesseren voor hun ontwikkeling.

ZuriHac: oefenen met functioneel programmeren

Foto van Twitter ZuriHac

Het schema is heel eenvoudig: u moet vooraf een paar voorstellen over uw project schrijven en deze naar de organisatoren sturen, die informatie over uw project op de evenementpagina zullen plaatsen. Bovendien hebben de auteurs van de projecten op de eerste dag dertig seconden de tijd om heel kort vanaf het podium te vertellen wat ze doen en wat er moet gebeuren. Vervolgens gaan geïnteresseerden op zoek naar de auteurs en vragen ze gedetailleerd naar de taken.

We hebben nog geen eigen openstaande projecten, maar we willen heel graag bijdragen aan bestaande projecten en daarom hebben we ons aangemeld als vaste deelnemer. Drie dagen lang hebben we met twee groepen ontwikkelaars gewerkt. Het blijkt dat de gezamenlijke studie van code en live communicatie de interactie tussen projectauteurs en bijdragers zeer productief maakt - bij ZuriHac konden we gebieden begrijpen die nieuw voor ons waren en konden we twee totaal verschillende teams helpen, waarbij we in elk team één taak voltooiden. van de projecten.

Naast waardevolle praktijkervaring werden er bij ZuriHac ook diverse lezingen en masterclasses gegeven. We herinneren ons vooral twee lezingen. In de eerste daarvan sprak Andrey Mokhov van de Universiteit van Newcastle over selectieve applicatieve functors - een klasse van typen die een intermediair zouden moeten worden tussen applicatieve functors en monaden. In een andere lezing sprak een van de oprichters van Haskell, Simon Peyton Jones, over hoe type-inferentie werkt in de GHC-compiler.

ZuriHac: oefenen met functioneel programmeren

Lezing door Simon Peyton Jones. Foto van Twitter ZuriHac

De masterclasses die tijdens de hackathon werden gegeven, waren onderverdeeld in drie categorieën, afhankelijk van het trainingsniveau van de deelnemers. De taken die werden aangeboden aan deelnemers die deelnamen aan de ontwikkeling van projecten, werden ook gemarkeerd met een moeilijkheidsgraad. De kleine maar vriendelijke gemeenschap van functionele programmeurs verwelkomt nieuwkomers graag in haar gelederen. Om de lezingen van Andrei Mokhov en Simon Peyton Jones te begrijpen, was de cursus functioneel programmeren die we aan de universiteit volgden echter erg nuttig.

Registratie voor het evenement is gratis voor zowel vaste deelnemers als projectauteurs. Begin juni dienden we aanvragen voor deelname in, waarna we snel van de wachtlijst overgingen naar de lijst met bevestigde deelnemers.

En nu zullen we het hebben over de projecten waaraan we hebben deelgenomen.

Pandoc

Pandoc is een universele converter van tekstdocumenten, in feite van elk formaat naar elk formaat. Bijvoorbeeld van docx naar pdf, of van Markdown naar MediaWiki. De auteur, John MacFarlane, is hoogleraar filosofie aan de Universiteit van Californië, Berkeley. Over het algemeen is Pandoc behoorlijk beroemd, en sommige van onze vrienden waren verrast toen ze hoorden dat Pandoc in Haskell was geschreven.

ZuriHac: oefenen met functioneel programmeren

Lijst met documentformaten die door Pandoc worden ondersteund. Er staat ook een hele grafiek op de site, maar deze afbeelding past niet in het artikel.

Uiteraard biedt Pandoc geen directe conversie voor elk paar formaten. Om zo'n grote verscheidenheid aan transformaties te ondersteunen, wordt een standaard architecturale oplossing gebruikt: eerst wordt het hele document vertaald naar een speciale interne tussenrepresentatie, en vervolgens wordt uit deze interne representatie een document in een ander formaat gegenereerd. De ontwikkelaars noemen de interne representatie “AST”, wat staat voor Abstract Syntax Tree, of abstracte syntaxisboom. Je kunt heel eenvoudig naar de tussenweergave kijken: je hoeft alleen maar het uitvoerformaat op “native” te zetten

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

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

Lezers die op zijn minst een beetje met Haskell hebben gewerkt, kunnen op basis van dit kleine voorbeeld al aannemen dat Pandoc in Haskell is geschreven: de uitvoer van dit commando is een stringrepresentatie van de interne structuren van Pandoc, gemaakt naar de gelijkenis van hoe het gewoonlijk wordt gedaan in Haskell, bijvoorbeeld in de standaardbibliotheek.

Hier kun je dus zien dat de interne representatie een recursieve structuur is, in elk intern knooppunt waarvan er een lijst is. Op het hoogste niveau is er bijvoorbeeld een lijst met één element: de kop van het eerste niveau met de attributen "hello-world",[],[]. Verborgen in deze koptekst is een lijst met de tekenreeks “Hallo”, gevolgd door een spatie en de tekenreeks “Wereld!”.

Zoals u kunt zien, verschilt de interne weergave niet veel van HTML. Het is een boom waarin elk intern knooppunt enige informatie geeft over de opmaak van zijn nakomelingen, en de bladeren bevatten de feitelijke inhoud van het document.

Als we naar het implementatieniveau gaan, wordt het gegevenstype voor het hele document als volgt gedefinieerd:

data Pandoc = Pandoc Meta [Block]

Hier is Block precies de hierboven genoemde interne hoekpunten, en Meta is meta-informatie over het document, zoals titel, aanmaakdatum, auteurs - dit is verschillend voor verschillende formaten, en Pandoc probeert, indien mogelijk, dergelijke informatie te behouden bij het vertalen van formaat naar formaat.

Bijna alle constructors van het Block-type - bijvoorbeeld Header of Para (paragraaf) - nemen attributen en een lijst met hoekpunten op een lager niveau als argumenten - Inline, in de regel. Space of Str zijn bijvoorbeeld constructors van het type Inline, en de HTML-tag verandert ook in zijn eigen speciale Inline. Wij zien het nut niet in van het geven van een volledige definitie van deze typen, maar houd er rekening mee dat deze hier te vinden is hier.

Interessant genoeg is het type Pandoc een monoïde. Dit betekent dat er een soort leeg document is en dat documenten op elkaar kunnen worden gestapeld. Dit is handig om te gebruiken bij het schrijven van Readers: u kunt een document in delen opsplitsen met behulp van willekeurige logica, elk afzonderlijk analyseren en vervolgens alles samenvoegen in één document. In dit geval wordt meta-informatie uit alle delen van het document in één keer verzameld.

Bij het converteren van bijvoorbeeld LaTeX naar HTML converteert een speciale module genaamd LaTeXReader eerst het invoerdocument naar AST, en vervolgens converteert een andere module genaamd HTMLWriter de AST naar HTML. Dankzij deze architectuur is het niet nodig om een ​​kwadratisch aantal conversies te schrijven - het is voldoende om Reader en Writer te schrijven voor elk nieuw formaat, en alle mogelijke conversieparen worden automatisch ondersteund.

Het is duidelijk dat een dergelijke architectuur ook zijn nadelen heeft, die al lang voorspeld worden door experts op het gebied van software-architectuur. Het belangrijkste zijn de kosten voor het aanbrengen van wijzigingen in de syntaxisboom. Als de wijziging ernstig genoeg is, moet u de code in alle lezers en schrijvers wijzigen. Een van de uitdagingen waarmee Pandoc-ontwikkelaars worden geconfronteerd, is bijvoorbeeld het ondersteunen van complexe tabelformaten. Nu kan Pandoc alleen heel eenvoudige tabellen maken, met een koptekst, kolommen en een waarde in elke cel. Het colspan-attribuut in HTML wordt bijvoorbeeld eenvoudigweg genegeerd. Een van de redenen voor dit gedrag is het ontbreken van een uniform schema voor het weergeven van tabellen in alle of op zijn minst veel formaten - dienovereenkomstig is het onduidelijk in welke vorm de tabellen moeten worden opgeslagen in de interne representatie. Maar zelfs nadat u een specifieke weergave hebt geselecteerd, moet u absoluut alle lezers en schrijvers wijzigen die het werken met tabellen ondersteunen.

De Haskell-taal werd niet alleen gekozen vanwege de grote liefde van de auteurs voor functioneel programmeren. Haskell staat bekend om zijn uitgebreide tekstverwerkingsmogelijkheden. Een voorbeeld is de bibliotheek parsec is een bibliotheek die actief gebruik maakt van de concepten van functioneel programmeren - monoïden, monaden, applicatieve en alternatieve functoren - om willekeurige parsers te schrijven. De volledige kracht van Parsec is te zien in voorbeeld van HaskellWiki, waar een complete parser van een eenvoudige imperatieve programmeertaal wordt geparseerd. Uiteraard wordt Parsec ook actief gebruikt in Pandoc.

Kort beschreven: monaden worden gebruikt voor sequentiële analyse, waarbij het ene eerst komt, en dan het andere. In dit voorbeeld bijvoorbeeld:

whileParser :: Parser Stmt
whileParser = whiteSpace >> statement

Eerst moet je de spatie tellen, en dan de instructie - die ook het Parser Stmt-type heeft.

Alternatieve functors worden gebruikt om terug te draaien als het parseren mislukt. Bijvoorbeeld,

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

Dit betekent dat u moet proberen de verklaring tussen haakjes te lezen, of meerdere verklaringen achter elkaar moet lezen.

Toepassingsfunctoren worden voornamelijk gebruikt als snelkoppelingen voor monaden. Laat de tok-functie bijvoorbeeld een token lezen (dit is een echte functie van LaTeXReader). Laten we naar deze combinatie kijken

const <$> tok <*> tok

Het leest twee tokens op rij en retourneert de eerste.

Voor al deze klassen heeft Haskell prachtige symbolische operatoren, waardoor Reader-programmering op ASCII-kunst lijkt. Bewonder gewoon deze prachtige code.

Onze taken hadden betrekking op LaTeXReader. Vasily's taak was het ondersteunen van de mbox- en hbox-opdrachten, handig voor het schrijven van pakketten in LaTeX. Elizabeth was verantwoordelijk voor de ondersteuning van het epigraph-commando, waarmee je epigraphs in LaTeX-documenten kunt maken.

Hatrace

UNIX-achtige besturingssystemen implementeren vaak de ptrace-systeemaanroep. Het is handig bij het debuggen en simuleren van programmaomgevingen, waardoor u de systeemaanroepen die het programma doet, kunt traceren. Het zeer nuttige hulpprogramma strace maakt bijvoorbeeld intern gebruik van ptrace.

Hatrace is een bibliotheek die een interface biedt voor ptrace in Haskell. Feit is dat ptrace zelf erg geavanceerd is en dat het vrij moeilijk is om het rechtstreeks te gebruiken, vooral vanuit functionele talen.

Hatrace werkt als strace bij het opstarten en accepteert soortgelijke argumenten. Het verschilt van strace doordat het ook een bibliotheek is die een eenvoudiger interface biedt dan alleen ptrace.

Met de hulp van hatrace hebben we al een onaangename bug in de GHC Haskell-compiler ontdekt: hij wordt op het verkeerde moment gedood, genereert onjuiste objectbestanden en compileert ze niet opnieuw wanneer hij opnieuw wordt opgestart. Scripting door middel van systeemaanroepen maakte het mogelijk om de fout betrouwbaar in één run te reproduceren, terwijl willekeurige kills de fout in ongeveer twee uur reproduceerden.

We hebben systeemoproepinterfaces aan de bibliotheek toegevoegd: Elizaveta heeft brk toegevoegd en Vasily heeft mmap toegevoegd. Op basis van de resultaten van ons werk is het mogelijk om de argumenten van deze systeemaanroepen eenvoudiger en nauwkeuriger te gebruiken bij het gebruik van de bibliotheek.

Bron: www.habr.com

Voeg een reactie