ZuriHac: practicando programación funcional

En junio de este año, en la pequeña ciudad suiza de Rapperswil, se llevó a cabo un evento llamado ZuriHac. Esta vez reunió a más de quinientos amantes de Haskell, desde principiantes hasta los padres fundadores del idioma. Aunque los organizadores llaman a este evento hackathon, no es una conferencia ni un hackathon en el sentido clásico. Su formato es diferente al de los programadores tradicionales. ¡Conocimos ZuriHac por suerte, participamos en él y ahora consideramos que es nuestro deber contarles sobre este hallazgo inusual!

ZuriHac: practicando programación funcional

sobre nosotros

Este artículo fue preparado por dos estudiantes de tercer año del programa “Matemáticas e Informática Aplicadas” de la Escuela Superior de Economía de la Universidad Nacional de Investigación de San Petersburgo: Vasily Alferov y Elizaveta Vasilenko. La pasión por la programación funcional para ambos comenzó con una serie de conferencias de D. N. Moskvin en el segundo año de la universidad. Vasily participa actualmente en el programa Google Summer of Code, dentro del cual implementa gráficos algebraicos en Haskell bajo la dirección del equipo del proyecto. alga. Elizaveta aplicó las habilidades de programación funcional adquiridas en el trabajo del curso dedicado a la implementación del algoritmo antiunificación con su posterior aplicación en teoría de tipos.

Formato del evento

El público objetivo son propietarios de proyectos de código abierto, programadores que quieran participar en su desarrollo, investigadores de programación funcional y personas simplemente apasionadas por Haskell. Este año, desarrolladores de más de cincuenta proyectos de código abierto de Haskell de todo el mundo se reunieron en el lugar, la HSR Hochschule für Technik Rapperswil, para hablar sobre sus productos y conseguir que nuevas personas se interesaran en su desarrollo.

ZuriHac: practicando programación funcional

Foto de Twitter ZuriHac

El esquema es muy simple: debes escribir algunas propuestas sobre tu proyecto con anticipación y enviarlas a los organizadores, quienes publicarán información sobre tu proyecto en la página del evento. Además, el primer día, los autores de los proyectos disponen de treinta segundos para contar muy brevemente desde el escenario qué están haciendo y qué hay que hacer. Luego, las personas interesadas buscan a los autores y preguntan detalladamente sobre las tareas.

Aún no tenemos nuestros propios proyectos abiertos, pero realmente queremos contribuir a los existentes, por eso nos registramos como participantes habituales. Durante tres días, trabajamos con dos grupos de desarrolladores. Resulta que el estudio conjunto del código y la comunicación en vivo hace que la interacción entre los autores del proyecto y los contribuyentes sea muy productiva: en ZuriHac pudimos comprender áreas que eran nuevas para nosotros y pudimos ayudar a dos equipos completamente diferentes, completando una tarea en cada uno. de los proyectos.

Además de una valiosa práctica, en ZuriHac también se impartieron varias conferencias y clases magistrales. Recordamos especialmente dos conferencias. En el primero de ellos, Andrey Mokhov de la Universidad de Newcastle habló sobre los funtores aplicativos selectivos, una clase de tipos que deberían convertirse en un intermediario entre los funtores aplicativos y las mónadas. En otra conferencia, uno de los fundadores de Haskell, Simon Peyton Jones, habló sobre cómo funciona la inferencia de tipos en el compilador GHC.

ZuriHac: practicando programación funcional

Conferencia de Simon Peyton Jones. Foto de Twitter ZuriHac

Las clases magistrales celebradas durante el hackathon se dividieron en tres categorías en función del nivel de formación de los participantes. Las tareas ofrecidas a los participantes que se sumaron al desarrollo de los proyectos también fueron marcadas con un nivel de dificultad. La pequeña pero amigable comunidad de programadores funcionales da la bienvenida a los recién llegados a sus filas. Sin embargo, para comprender las conferencias de Andrei Mokhov y Simon Peyton Jones, el curso de programación funcional que tomamos en la universidad fue muy útil.

La inscripción al evento es gratuita tanto para los participantes habituales como para los autores de proyectos. Presentamos solicitudes de participación a principios de junio, después de lo cual rápidamente fuimos transferidos de la lista de espera a la lista de participantes confirmados.

Y ahora hablaremos de los proyectos en cuyo desarrollo participamos.

Pandoc

Pandoc es un conversor universal de documentos de texto, de hecho, de cualquier formato a cualquier. Por ejemplo, de docx a pdf, o de Markdown a MediaWiki. Su autor, John MacFarlane, es profesor de filosofía en la Universidad de California, Berkeley. En general, Pandoc es bastante famoso y algunos de nuestros amigos se sorprendieron cuando supieron que Pandoc estaba escrito en Haskell.

ZuriHac: practicando programación funcional

Lista de formatos de documentos soportados por Pandoc. También hay un gráfico completo en el sitio, pero esta imagen no encaja en el artículo.

Por supuesto, Pandoc no proporciona conversión directa para cada par de formatos. Para admitir una variedad tan amplia de transformaciones, se utiliza una solución arquitectónica estándar: primero, todo el documento se traduce a una representación intermedia interna especial y luego se genera un documento en un formato diferente a partir de esta representación interna. Los desarrolladores llaman a la representación interna "AST", que significa árbol de sintaxis abstracta, o árbol de sintaxis abstracta. Puedes ver la representación intermedia de manera muy simple: todo lo que necesitas hacer es configurar el formato de salida en "nativo".

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

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

Los lectores que han trabajado al menos un poco con Haskell ya pueden suponer a partir de este pequeño ejemplo que Pandoc está escrito en Haskell: la salida de este comando es una representación en cadena de las estructuras internas de Pandoc, creada a semejanza de cómo se hace habitualmente. en Haskell, por ejemplo, en la biblioteca estándar.

Entonces, aquí puede ver que la representación interna es una estructura recursiva, en cada nodo interno del cual hay una lista. Por ejemplo, en el nivel superior hay una lista de un elemento: el encabezado del primer nivel con los atributos "hola-mundo",[],[]. Oculta dentro de este encabezado hay una lista de la cadena "Hola", seguida de un espacio y la cadena "¡Mundo!".

Como puede ver, la representación interna no es muy diferente de HTML. Es un árbol donde cada nodo interno proporciona información sobre el formato de sus descendientes y las hojas contienen el contenido real del documento.

Si bajamos al nivel de implementación, el tipo de datos para todo el documento se define así:

data Pandoc = Pandoc Meta [Block]

Aquí Block son precisamente los vértices internos mencionados anteriormente, y Meta es metainformación sobre el documento, como título, fecha de creación, autores; esto es diferente para diferentes formatos, y Pandoc intenta, si es posible, preservar dicha información al traducir de un formato a otro. formato.

Casi todos los constructores del tipo Bloque, por ejemplo, Encabezado o Para (párrafo), toman atributos y una lista de vértices de nivel inferior como argumentos, en línea, por regla general. Por ejemplo, Space o Str son constructores del tipo Inline, y la etiqueta HTML también se convierte en su propio Inline especial. No vemos ningún sentido en proporcionar una definición completa de estos tipos, pero tenga en cuenta que se puede encontrar aquí. aquí.

Curiosamente, el tipo Pandoc es un monoide. Esto significa que hay algún tipo de documento vacío y que los documentos se pueden apilar. Esto es conveniente de usar al escribir Readers: puede dividir un documento en partes usando lógica arbitraria, analizar cada una por separado y luego juntar todo en un solo documento. En este caso, la metainformación se recopilará de todas las partes del documento a la vez.

Al convertir, digamos, de LaTeX a HTML, primero un módulo especial llamado LaTeXReader convierte el documento de entrada a AST, luego otro módulo llamado HTMLWriter convierte el AST a HTML. Gracias a esta arquitectura, no es necesario escribir un número cuadrático de conversiones; basta con escribir Reader y Writer para cada nuevo formato, y todos los pares posibles de conversiones serán compatibles automáticamente.

Está claro que esta arquitectura también tiene sus inconvenientes, predichos desde hace mucho tiempo por los expertos en el campo de la arquitectura de software. El más importante es el coste de realizar cambios en el árbol de sintaxis. Si el cambio es lo suficientemente grave, tendrás que cambiar el código en todos los Lectores y Escritores. Por ejemplo, uno de los desafíos que enfrentan los desarrolladores de Pandoc es admitir formatos de tablas complejos. Ahora Pandoc sólo puede crear tablas muy simples, con un encabezado, columnas y un valor en cada celda. Por ejemplo, el atributo colspan en HTML simplemente se ignorará. Una de las razones de este comportamiento es la falta de un esquema unificado para representar tablas en todos o al menos muchos formatos; por lo tanto, no está claro en qué forma deben almacenarse las tablas en la representación interna. Pero incluso después de seleccionar una vista específica, deberá cambiar absolutamente todos los lectores y escritores que admitan el trabajo con tablas.

Se eligió el lenguaje Haskell no solo por el gran amor de los autores por la programación funcional. Haskell es conocido por sus amplias capacidades de procesamiento de texto. Un ejemplo es la biblioteca. pársec es una biblioteca que utiliza activamente los conceptos de programación funcional (monoides, mónadas, funtores aplicativos y alternativos) para escribir analizadores arbitrarios. Todo el poder de Parsec se puede ver en ejemplo de HaskellWiki, donde se analiza un analizador completo de un lenguaje de programación imperativo simple. Por supuesto, Parsec también se utiliza activamente en Pandoc.

Descrito brevemente, las mónadas se utilizan para el análisis secuencial, cuando una cosa aparece primero y luego otra. Por ejemplo, en este ejemplo:

whileParser :: Parser Stmt
whileParser = whiteSpace >> statement

Primero debe contar el espacio y luego la declaración, que también tiene el tipo Parser Stmt.

Se utilizan funtores alternativos para revertir si falla el análisis. Por ejemplo,

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

Significa que debe intentar leer la declaración entre paréntesis o intentar leer varias declaraciones secuencialmente.

Los funtores aplicativos se utilizan principalmente como atajos para mónadas. Por ejemplo, deje que la función tok lea algún token (esta es una función real de LaTeXReader). Veamos esta combinación.

const <$> tok <*> tok

Leerá dos tokens seguidos y devolverá el primero.

Para todas estas clases, Haskell tiene hermosos operadores simbólicos, lo que hace que la programación de Reader parezca arte ASCII. Simplemente admire este maravilloso código.

Nuestras tareas estaban relacionadas con LaTeXReader. La tarea de Vasily era soportar los comandos mbox y hbox, útiles para escribir paquetes en LaTeX. Elizabeth fue responsable de respaldar el comando epigraph, que permite crear epígrafes en documentos LaTeX.

Hatrace

Los sistemas operativos tipo UNIX a menudo implementan la llamada al sistema ptrace. Es útil para depurar y simular entornos de programas, permitiéndole rastrear las llamadas al sistema que realiza el programa. Por ejemplo, la muy útil utilidad strace utiliza ptrace internamente.

Hatrace es una biblioteca que proporciona una interfaz para ptrace en Haskell. El hecho es que ptrace en sí es muy sofisticado y es bastante difícil usarlo directamente, especialmente desde lenguajes funcionales.

Hatrace se ejecuta como strace al inicio y acepta argumentos similares. Se diferencia de strace en que también es una biblioteca que proporciona una interfaz más simple que simplemente ptrace.

Con la ayuda de hatrace, ya hemos detectado un error desagradable en el compilador GHC Haskell: al ser eliminado en el momento equivocado, genera archivos de objetos incorrectos y no los vuelve a compilar cuando se reinicia. La secuencia de comandos mediante llamadas al sistema hizo posible reproducir de manera confiable el error en una sola ejecución, mientras que las eliminaciones aleatorias reprodujeron el error en aproximadamente dos horas.

Agregamos interfaces de llamadas al sistema a la biblioteca: Elizaveta agregó brk y Vasily agregó mmap. Según los resultados de nuestro trabajo, es posible utilizar de forma más sencilla y precisa los argumentos de estas llamadas al sistema al utilizar la biblioteca.

Fuente: habr.com

Añadir un comentario