ZuriHac : pratiquer la programmation fonctionnelle

En juin de cette année, dans la petite ville suisse de Rapperswil, un événement appelé ZuriHac. Cette fois, il a réuni plus de cinq cents amateurs de Haskell, des débutants aux pères fondateurs de la langue. Bien que les organisateurs appellent cet événement un hackathon, il ne s’agit pas d’une conférence ou d’un hackathon au sens classique du terme. Son format est différent des programmeurs traditionnels. Nous avons découvert ZuriHac par hasard, y avons participé et maintenant nous considérons qu'il est de notre devoir de raconter cette découverte insolite !

ZuriHac : pratiquer la programmation fonctionnelle

Qui sommes-nous

Cet article a été préparé par deux étudiants de 3ème année du programme « Mathématiques appliquées et informatique » de l'École supérieure d'économie de l'Université nationale de recherche 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é. Vasily participe actuellement au programme Google Summer of Code, dans le cadre duquel il implémente des graphes algébriques en Haskell sous la direction de l'équipe du projet. Algue. Elizaveta a appliqué les compétences acquises en programmation fonctionnelle dans le cadre de cours consacrés à la mise en œuvre de l'algorithme anti-unification avec application ultérieure à la théorie des types.

Format de l'événement

Le public cible est constitué de propriétaires de projets open source, de programmeurs souhaitant participer à leur développement, de chercheurs en programmation fonctionnelle et de personnes simplement passionnées par Haskell. Cette année, à la HSR Hochschule für Technik Rapperswil, les développeurs de plus de cinquante projets Haskell open source du monde entier se sont réunis pour parler de leurs produits et intéresser de nouvelles personnes à leur développement.

ZuriHac : pratiquer la programmation fonctionnelle

Photo de Twitter ZuriHac

Le schéma est très simple : vous devez rédiger à l'avance quelques propositions 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 disposent de trente secondes pour expliquer très brièvement depuis la scène ce qu'ils font et ce qu'il faut faire. Ensuite, les personnes intéressées recherchent les auteurs et posent des questions détaillées sur les tâches.

Nous n’avons pas encore nos propres projets ouverts, mais nous souhaitons vraiment contribuer à ceux existants, c’est pourquoi nous nous sommes inscrits en tant que participants réguliers. Pendant trois jours, nous avons travaillé avec deux groupes de développeurs. Il s'avère que l'étude conjointe du code et de la communication en direct rend l'interaction entre les auteurs du projet et les contributeurs très productive - chez ZuriHac, nous avons pu comprendre des domaines qui étaient nouveaux pour nous et aider deux équipes complètement différentes, en accomplissant une tâche dans chacune. des projets.

En plus de précieuses pratiques, plusieurs conférences et master classes ont également été données au ZuriHac. Nous nous souvenons particulièrement de deux conférences. Lors du premier d'entre eux, Andrey Mokhov de l'Université de Newcastle a parlé des foncteurs applicatifs sélectifs - une classe de types qui devraient devenir intermédiaires entre les foncteurs applicatifs et les monades. Dans une autre conférence, l'un des fondateurs de Haskell, Simon Peyton Jones, a expliqué comment fonctionne l'inférence de type dans le compilateur GHC.

ZuriHac : pratiquer la programmation fonctionnelle

Conférence de Simon Peyton Jones. Photo de Twitter ZuriHac

Les master classes organisées lors du hackathon ont été divisées en trois catégories en fonction du niveau de formation des participants. Les tâches proposées aux participants qui ont participé au développement des projets étaient également marquées par un niveau de difficulté. La petite mais sympathique communauté de programmeurs fonctionnels accueille avec plaisir les nouveaux arrivants dans ses rangs. Cependant, pour comprendre les cours d'Andrei Mokhov et de Simon Peyton Jones, le cours de programmation fonctionnelle que nous avons suivi à l'université s'est avéré très utile.

L'inscription à l'événement est gratuite pour les participants réguliers et les auteurs de projets. Nous avons déposé les demandes de participation 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 auxquels nous avons participé.

pandoc

pandoc est un convertisseur universel de documents texte, en fait, de n'importe quel format vers n'importe quel. Par exemple, de docx à 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 lorsqu'ils ont appris que Pandoc était écrit en Haskell.

ZuriHac : pratiquer la programmation fonctionnelle

Liste des formats de documents pris en charge par Pandoc. Il y a aussi un graphique complet sur le site, mais cette image ne rentre pas dans l'article.

Bien entendu, Pandoc ne propose pas de conversion directe pour chaque paire de formats. Pour prendre en charge une telle variété de transformations, une solution architecturale standard est utilisée : 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 : il suffit de définir le format de sortie sur « natif »

$ 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 en Haskell : le résultat de cette commande est une représentation sous forme de chaîne des structures internes de Pandoc, créée à l'image de la façon dont cela est habituellement fait. en Haskell, par exemple dans la bibliothèque standard.

Ainsi, vous pouvez voir ici que la représentation interne est une structure récursive, dans chaque nœud interne de laquelle se trouve une liste. Par exemple, au niveau supérieur, il y a une liste d'un élément - l'en-tête de premier niveau avec les attributs "hello-world",[],[]. Caché à l'intérieur de cet en-tête se trouve une liste de la chaîne « Bonjour », suivie d'un espace et de la chaîne « Monde ! ».

Comme vous pouvez le constater, la représentation interne n’est pas très différente du HTML. Il s'agit d'un arbre dans lequel chaque nœud interne fournit des informations sur le formatage de ses descendants, et les feuilles contiennent le contenu réel du document.

Si l'on descend au niveau de l'implémentation, le type de données pour l'ensemble du document est défini comme ceci :

data Pandoc = Pandoc Meta [Block]

Ici, Block correspond précisément aux sommets internes mentionnés ci-dessus, et Meta correspond à des métainformations sur le document, telles que le titre, la date de création, les auteurs - ceci est différent selon les formats, et Pandoc essaie, si possible, de conserver ces informations lors de la traduction du format vers format.

Presque tous les constructeurs de type Block - par exemple, Header ou Para (paragraphe) - prennent comme arguments des attributs et une liste de sommets de niveau inférieur - Inline, en règle générale. Par exemple, Space ou Str sont des constructeurs de type Inline, et la balise HTML se transforme également en son propre Inline spécial. Nous ne voyons pas l’intérêt de fournir une définition complète de ces types, mais notons qu’elle peut être trouvée ici 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. Ceci est pratique à utiliser lors de l'écriture de Readers - vous pouvez diviser un document en parties en utilisant une logique arbitraire, analyser chacune séparément, puis tout rassembler en un seul document. Dans ce cas, les métainformations seront collectées simultanément 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 l'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 qu'une telle architecture a aussi ses inconvénients, prédits depuis longtemps par les experts dans le domaine de l'architecture logicielle. Le plus important est le coût des modifications apportées à l’arbre syntaxique. Si le changement est suffisamment grave, vous devrez modifier le code dans tous les lecteurs et écrivains. Par exemple, l'un des défis auxquels sont confrontés les développeurs de Pandoc est la prise en charge de formats de tableaux complexes. Désormais Pandoc ne peut créer que des tableaux très simples, avec un en-tête, des colonnes et une valeur dans chaque cellule. Par exemple, l'attribut colspan en HTML sera simplement ignoré. L'une des raisons de ce comportement est l'absence d'un schéma unifié pour représenter les tableaux dans tous ou au moins plusieurs formats - par conséquent, il n'est pas clair sous quelle forme les tableaux doivent être stockés dans la représentation interne. Mais même après avoir sélectionné une vue spécifique, vous devrez modifier absolument tous les lecteurs et rédacteurs prenant en charge l'utilisation des tableaux.

Le langage Haskell n’a pas été choisi uniquement en raison du grand amour des auteurs pour la programmation fonctionnelle. Haskell est connu pour ses capacités étendues de traitement de texte. Un exemple est la bibliothèque parsec est une bibliothèque qui utilise activement les concepts de programmation fonctionnelle - monoïdes, monades, foncteurs applicatifs et alternatifs - pour écrire des analyseurs arbitraires. Toute la puissance du Parsec peut être vue dans exemple de HaskellWiki, où un analyseur complet d'un langage de programmation impératif simple est analysé. Bien entendu, Parsec est également activement utilisé dans Pandoc.

Brièvement décrites, les monades sont utilisées pour l'analyse séquentielle, lorsqu'une chose vient en premier, puis une autre. Par exemple, dans cet exemple :

whileParser :: Parser Stmt
whileParser = whiteSpace >> statement

Vous devez d'abord compter l'espace, puis l'instruction - qui a également le type Parser Stmt.

Des foncteurs alternatifs sont utilisés pour revenir en arrière si l'analyse échoue. Par exemple,

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

Cela signifie que vous devez soit essayer de lire la déclaration entre parenthèses, soit essayer de lire plusieurs déclarations séquentiellement.

Les foncteurs applicatifs sont principalement utilisés comme raccourcis pour les monades. Par exemple, laissez la fonction tok lire un jeton (c'est une vraie fonction de LaTeXReader). Regardons cette combinaison

const <$> tok <*> tok

Il lira deux jetons d'affilée et renverra le premier.

Pour toutes ces classes, Haskell dispose de superbes opérateurs symboliques, ce qui fait que la programmation Reader ressemble à 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 pour écrire des packages en LaTeX. Elizabeth était chargée de prendre en charge la commande epigraph, qui vous permet de créer des épigraphes dans les documents LaTeX.

Course de haine

Les systèmes d'exploitation de type UNIX implémentent souvent l'appel système ptrace. Il est utile pour le débogage et la simulation des environnements de programme, vous permettant de tracer les appels système effectués par le programme. Par exemple, le très utile utilitaire strace utilise ptrace en interne.

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, notamment à partir de langages fonctionnels.

Hatrace fonctionne comme strace au démarrage et accepte des arguments similaires. Il diffère de strace en ce sens qu'il s'agit également d'une bibliothèque qui fournit une interface plus simple que ptrace.

Avec l'aide de Hatrace, nous avons déjà détecté un bug désagréable dans le compilateur GHC Haskell : étant tué au mauvais moment, il génère des fichiers objets incorrects et ne les recompile pas au redémarrage. Les scripts par appels système ont permis de reproduire l'erreur de manière fiable en une seule exécution, tandis que les suppressions aléatoires reproduisaient l'erreur en deux heures environ.

Nous avons ajouté des interfaces d'appel système à la bibliothèque - Elizaveta a ajouté brk et Vasily a ajouté mmap. Sur la base des résultats de nos travaux, il est possible d'utiliser plus simplement et plus précisément les arguments de ces appels système lors de l'utilisation de la bibliothèque.

Source: habr.com

Ajouter un commentaire