En juin de cette année, un événement appelé
ZuriHac a eu lieu pour la dixième fois dans la petite ville suisse de Rapperswil. Cette fois, plus de cinq cents amants Haskell se sont réunis des débutants aux pères fondateurs de la langue. Bien que les organisateurs appellent cet événement un hackathon, il ne s'agit toujours pas d'une conférence ou d'un hackathon au sens classique. Son format est différent de la programmation traditionnelle. Nous avons découvert ZuriHac par une heureuse coïncidence, y avons participé, et maintenant nous considérons qu'il est de notre devoir de parler d'une découverte inhabituelle!

À propos de nous
Cet article a été préparé par deux étudiants de 3e année du programme de mathématiques appliquées et d'informatique de l'École supérieure d'économie de Saint-Pétersbourg: Vasily Alferov et Elizaveta Vasilenko. La passion pour la programmation fonctionnelle pour nous deux a commencé avec une série de conférences de D.N. Moskvin en 2e année d'université. Actuellement, Vasily participe au programme Google Summer of Code, dans le cadre duquel il est engagé dans la mise en œuvre de graphes algébriques en langage Haskell sous la direction de l'
équipe de projet
Alga . Elizabeth a appliqué les compétences acquises de la programmation fonctionnelle dans les travaux de cours consacrés à la mise en œuvre de l'algorithme anti-unification avec une utilisation ultérieure dans la théorie des types.
Format de l'événement
Le public cible est les propriétaires de projets open source, les programmeurs qui souhaitent participer à leur développement, les chercheurs en programmation fonctionnelle et uniquement les personnes passionnées par Haskell. Cette année, le HSR Hochschule für Technik Rapperswil University a réuni des développeurs de plus de cinquante projets Haskell open source du monde entier pour parler de leurs produits et intéresser les nouvelles personnes à leur développement.
Photo de Twitter ZuriHacLe schéma est très simple: vous devez écrire quelques propositions à l'avance sur votre projet et les envoyer aux organisateurs, qui publieront des informations sur votre projet sur la page de l'événement. De plus, le premier jour, les auteurs des projets ont trente secondes pour dire très brièvement de la scène ce qu'ils font et ce qui doit être fait. Ensuite, les personnes intéressées recherchent des auteurs et demandent des détails sur les tâches.
Nous n'avons pas encore nos propres projets ouverts, mais nous voulons vraiment contribuer aux projets existants, nous nous sommes donc inscrits en tant que participants réguliers. Pendant trois jours, nous avons travaillé avec deux équipes de développement. Il s'avère que l'étude conjointe du code et de la communication en direct rend l'interaction des auteurs du projet et des contributeurs très productive - chez ZuriHac, nous avons pu découvrir de nouveaux domaines pour nous et avons pu aider deux équipes complètement différentes en clôturant la tâche dans chacun des projets.
En plus d'une pratique précieuse, plusieurs conférences et master classes ont également été données à ZuriHac. On se souvient surtout de deux conférences. Lors de la première d'entre elles, Andrei Mokhov de l'Université de Newcastle a parlé des foncteurs applicatifs sélectifs - une classe de type qui devrait devenir intermédiaire entre les foncteurs applicatifs et les monades. Dans une autre conférence, l'un des fondateurs de Haskell, Simon Peyton Jones, a parlé du fonctionnement de l'inférence de type dans le compilateur GHC.
Conférence de Simon Peyton Jones. Photo de Twitter ZuriHacLes master classes organisées pendant le hackathon ont été divisées en trois catégories selon le niveau de formation des participants. Les tâches proposées aux participants qui ont rejoint le développement de projets avaient également des notes avec des niveaux de difficulté. La petite mais sympathique communauté de programmeurs fonctionnels est heureuse d'accueillir les nouveaux venus dans leurs rangs. Pour comprendre les cours d'Andrei Mokhov et Simon Peyton Jones, cependant, le cours de programmation fonctionnelle passé à l'université nous a été très utile.
Pour les participants ordinaires et les auteurs de projets, l'inscription à l'événement est gratuite. Nous avons demandé à participer début juin, après quoi nous avons été rapidement transférés de la liste d'attente à la liste des participants confirmés.
Et maintenant nous allons parler des projets au développement desquels nous avons participé.
Pandoc
Pandoc est un convertisseur universel de documents texte, en fait - de n'importe quel format à n'importe quel. Par exemple, de docx en pdf, ou de Markdown à MediaWiki. Son auteur, John MacFarlane, est professeur de philosophie à l'Université de Californie à Berkeley. En général, Pandoc est assez célèbre, et certains de nos amis ont été surpris en apprenant que Pandoc était écrit en Haskell.
Liste des formats de document pris en charge par Pandoc. Le site a également un graphique complet, mais cette image ne rentre pas dans l'article.Bien entendu, Pandoc n'implémente pas de conversion directe pour chaque paire de formats. Pour prendre en charge un ensemble de transformations aussi étendu, une solution architecturale standard est utilisée: tout d'abord, le document entier est traduit en une représentation intermédiaire interne spéciale, puis un document dans un format différent est généré à partir de cette représentation interne. Les développeurs appellent la représentation interne "AST", qui signifie Abstract Syntax Tree, ou
arbre de syntaxe abstraite . Vous pouvez regarder la représentation intermédiaire très simplement: pour cela, il vous suffit de définir «natif» comme format de sortie
$ cat example.html <h1>Hello, World!</h1> $ pandoc -f html -t native example.html [Header 1 ("hello-world",[],[]) [Str "Hello,",Space,Str "World!"]]
Les lecteurs qui ont au moins un peu travaillé avec Haskell peuvent déjà supposer à partir de ce petit exemple que Pandoc est écrit spécifiquement en Haskell: la sortie de cette commande est une représentation de la structure interne de Pandoc sous forme de chaîne, créée de la même manière que d'habitude dans Haskell, par exemple, dans la bibliothèque standard.
Donc, ici, vous pouvez voir que la représentation interne est une structure récursive, dans chaque nœud interne dont il y a une liste. Par exemple, au niveau le plus élevé, il y a une liste d'un élément - l'en-tête de premier niveau avec les attributs «hello-world», [], []. À l'intérieur de cet en-tête se trouve une liste de la chaîne «Bonjour», un espace et la chaîne «Monde!».
Comme vous pouvez le voir, la représentation interne n'est pas très différente de HTML. Il s'agit d'un arbre, où chaque nœud interne rapporte des informations sur le formatage de ses descendants et les feuilles contiennent le contenu réel du document.
Si vous descendez au niveau d'une implémentation spécifique, le type de données pour l'ensemble du document est défini comme suit:
data Pandoc = Pandoc Meta [Block]
Ici, Block est précisément les pics internes mentionnés ci-dessus, et Meta est des méta-informations sur le document, telles que le titre, la date de création, les auteurs - cela est différent pour différents formats, et Pandoc essaie d'enregistrer ces informations chaque fois que possible lors du transfert d'un format à un autre.
Presque tous les constructeurs du type Block - par exemple, Header ou Para (paragraphe) - prennent des attributs et une liste de sommets d'un niveau inférieur - Inline, en règle générale, comme arguments. Par exemple, Space ou Str sont des concepteurs du type Inline, et la balise HTML est également convertie en son Inline spécial. Nous ne voyons aucune raison de donner une définition complète de ces types, cependant, nous notons que cela peut être vu
ici .
Fait intéressant, le type Pandoc est un monoïde. Cela signifie qu'il existe une sorte de document vide et que les documents peuvent être empilés entre eux. C'est pratique à utiliser lors de l'écriture de lecteurs - vous pouvez diviser un document en plusieurs parties avec une logique arbitraire, analyser chacune individuellement, puis rassembler le tout dans un seul document. Dans ce cas, les méta-informations seront collectées à la fois dans toutes les parties du document.
Lors de la conversion, par exemple, de LaTeX en HTML, un module spécial appelé LaTeXReader convertit d'abord le document d'entrée en AST, puis un autre module appelé HTMLWriter convertit AST en HTML. Grâce à cette architecture, il n'est pas nécessaire d'écrire un nombre quadratique de conversions - il suffit d'écrire Reader et Writer pour chaque nouveau format, et toutes les paires de conversions possibles seront automatiquement prises en charge.
Il est clair que cette architecture a aussi ses inconvénients, longtemps prédits par les experts dans le domaine de l'architecture logicielle. Le plus important est le coût des modifications apportées à l'arbre de syntaxe. Si le changement est suffisamment sérieux, vous devrez changer le code dans tous les lecteurs et écrivains. Par exemple, l'un des défis auxquels sont confrontés les développeurs Pandoc est de prendre en charge des formats de table complexes. Maintenant, Pandoc ne peut que dans les tableaux les plus simples, avec un en-tête, des colonnes et une valeur dans chaque cellule. Supposons que l'attribut colspan en HTML sera simplement ignoré. L'une des raisons de ce comportement est l'absence d'un schéma de représentation de table unique dans tous ou au moins de nombreux formats - en conséquence, il n'est pas clair dans quel formulaire les tables doivent être stockées dans la représentation interne. Mais même après avoir choisi une vue spécifique, il sera nécessaire de changer absolument tous les lecteurs et écrivains qui prennent en charge l'utilisation des tables.
Haskell a été choisi non seulement parmi un grand amour des auteurs pour la programmation fonctionnelle. Haskell est connu pour ses puissantes capacités de traitement de texte. Un exemple est la bibliothèque
parsec - une bibliothèque qui utilise activement les concepts de programmation fonctionnelle - monoïdes, monades, foncteurs applicatifs et alternatifs - pour écrire des analyseurs arbitraires. La pleine puissance de Parsec peut être vue dans l'
exemple HaskellWiki, qui analyse le parseur complet d'un langage de programmation impératif simple. Bien sûr, Parsec est également activement utilisé dans Pandoc.
En bref, les monades sont utilisées pour l'analyse séquentielle lorsque l'une vient en premier puis l'autre. Par exemple, dans cet exemple:
whileParser :: Parser Stmt whileParser = whiteSpace >> statement
Vous devez d'abord considérer un espace, puis une instruction - qui a également le type Parser Stmt.
Des foncteurs alternatifs sont utilisés pour revenir en arrière en cas d'échec de l'analyse. Par exemple
statement :: Parser Stmt statement = parens statement <|> sequenceOfStmt
Signifie que vous devez soit essayer de lire l'instruction entre parenthèses, soit essayer séquentiellement de lire plusieurs instructions.
Les foncteurs applicatifs sont principalement utilisés comme raccourcis pour les monades. Par exemple, laissez la fonction tok lire une sorte de jeton (c'est la vraie fonction de LaTeXReader). Regardons une telle combinaison
const <$> tok <*> tok
Elle lira deux jetons d'affilée et retournera le premier d'entre eux.
Haskell possède de magnifiques opérateurs symboliques pour toutes ces classes, ce qui fait que les lecteurs de programmation ressemblent à de l'art ASCII. Admirez simplement ce merveilleux code.
Nos tâches étaient liées à LaTeXReader. La tâche de Vasily était de prendre en charge les commandes \ mbox et \ hbox, utiles lors de l'écriture de packages dans LaTeX. Elizabeth était responsable du soutien de l'équipe \ epigraph, qui permet l'exécution des épigraphes dans les documents LaTeX.
Hatrace
Sur les systèmes d'exploitation de type UNIX, l'appel système ptrace est souvent implémenté. Il est utile pour déboguer et simuler des environnements de programme, vous permettant de suivre les appels système que le programme effectue. Par exemple, l'utilitaire strace très utile utilise ptrace en lui-même.
Hatrace est une bibliothèque qui fournit une interface pour ptrace dans Haskell. Le fait est que ptrace lui-même est très sophistiqué et qu'il est assez difficile de l'utiliser directement, en particulier à partir de langages fonctionnels.
Hatrace au démarrage fonctionne comme strace et accepte des arguments similaires. Sa différence avec strace est qu'il s'agit également d'une bibliothèque offrant une interface plus simple que juste ptrace.
Hatrace a déjà détecté un bogue désagréable dans le compilateur Haskell GHC - lorsqu'il est tué au mauvais moment, il génère des fichiers objets incorrects et ne les recompile pas au redémarrage. Le scripting sur les appels système a permis de reproduire l'erreur en une seule fois, alors que des tueries aléatoires ont reproduit l'erreur en environ deux heures.
Nous avons ajouté des interfaces d'appel système à la bibliothèque - Elizabeth a ajouté brk et Vasily a ajouté mmap. Selon les résultats de notre travail, nous pouvons utiliser plus facilement et plus précisément les arguments de ces appels système lors de l'utilisation de la bibliothèque.