ZuriHac: práctica de programación funcional

En xuño deste ano, na pequena cidade suíza de Rapperswil, convocou un evento ZuriHac. Nesta ocasión reuniu a máis de cincocentos amantes de Haskell, desde principiantes ata pais fundadores da lingua. Aínda que os organizadores denominan a este evento un hackathon, non é unha conferencia nin un hackathon no sentido clásico. O seu formato é diferente dos programadores tradicionais. Soubemos de ZuriHac por sorte, participamos nel e agora consideramos que é o noso deber contar o inusual achado!

ZuriHac: práctica de programación funcional

Quen somos

Este artigo foi preparado por dous estudantes de 3º curso do programa "Matemáticas e Informática Aplicadas" da Escola Superior de Economía da Universidade Nacional de Investigación - San Petersburgo: Vasily Alferov e Elizaveta Vasilenko. A paixón pola programación funcional para ambos comezou cunha serie de conferencias de D. N. Moskvin no 2º curso universitario. Vasily participa actualmente no programa Google Summer of Code, no que está a implementar gráficos alxébricos en Haskell baixo a dirección do equipo do proxecto. Alga. Elizaveta aplicou as habilidades de programación funcional adquiridas no traballo do curso dedicado á implementación do algoritmo anti-unificación coa posterior aplicación na teoría de tipos.

Formato do evento

O público obxectivo son propietarios de proxectos de código aberto, programadores que queren participar no seu desenvolvemento, investigadores de programación funcional e persoas simplemente apaixonadas por Haskell. Este ano, desenvolvedores de máis de cincuenta proxectos Haskell de código aberto de todo o mundo reuníronse no lugar - a HSR Hochschule für Technik Rapperswil - para falar dos seus produtos e facer que a xente nova se interese polo seu desenvolvemento.

ZuriHac: práctica de programación funcional

Foto de Twitter ZuriHac

O esquema é moi sinxelo: cómpre escribir algunhas propostas sobre o seu proxecto con antelación e envialas aos organizadores, que publicarán información sobre o seu proxecto na páxina do evento. Ademais, o primeiro día, os autores dos proxectos teñen trinta segundos para contar moi brevemente dende o escenario o que están a facer e o que hai que facer. Despois as persoas interesadas buscan aos autores e preguntan polo miúdo polas tarefas.

Aínda non temos os nosos propios proxectos abertos, pero realmente queremos contribuír aos existentes, polo que rexistrámonos como participantes habituais. Ao longo de tres días, traballamos con dous grupos de desenvolvedores. Resulta que o estudo conxunto do código e a comunicación en directo fai que a interacción entre os autores do proxecto e os colaboradores sexa moi produtiva: en ZuriHac puidemos comprender áreas que eran novas para nós e puidemos axudar a dous equipos completamente diferentes, completando unha tarefa en cada un. dos proxectos.

Ademais da práctica valiosa, tamén se impartiron varias conferencias e clases maxistrais en ZuriHac. Lembramos especialmente dúas conferencias. No primeiro deles, Andrey Mokhov da Universidade de Newcastle falou sobre os funtores aplicativos selectivos, unha clase de tipos que deberían converterse en intermediarios entre os funtores aplicativos e as mónadas. Noutra conferencia, un dos fundadores de Haskell, Simon Peyton Jones, falou sobre como funciona a inferencia de tipos no compilador GHC.

ZuriHac: práctica de programación funcional

Conferencia de Simon Peyton Jones. Foto de Twitter ZuriHac

As clases maxistrais realizadas durante o hackathon dividíronse en tres categorías dependendo do nivel de formación dos participantes. As tarefas ofertadas aos participantes que se sumaron ao desenvolvemento dos proxectos tamén estiveron marcadas cun nivel de dificultade. A pequena pero amigable comunidade de programadores funcionais dá a benvida aos recén chegados ás súas filas. Para entender as conferencias de Andrei Mokhov e Simon Peyton Jones, non obstante, o curso de programación funcional que fixemos na universidade foi moi útil.

A inscrición no evento é gratuíta tanto para os participantes habituais como para os autores do proxecto. Presentamos as solicitudes de participación a principios de xuño, despois de que nos trasladaron rapidamente da lista de espera á lista de participantes confirmados.

E agora falaremos dos proxectos no desenvolvemento dos que participamos.

PANDOC.

PANDOC. é un conversor universal de documentos de texto, de feito, de calquera formato a calquera. Por exemplo, de docx a pdf, ou de Markdown a MediaWiki. O seu autor, John MacFarlane, é profesor de filosofía na Universidade de California, Berkeley. En xeral, Pandoc é bastante famoso, e algúns dos nosos amigos quedaron sorprendidos cando souberon que Pandoc estaba escrito en Haskell.

ZuriHac: práctica de programación funcional

Lista de formatos de documentos admitidos por Pandoc. Tamén hai un gráfico completo no sitio, pero esta imaxe non encaixa no artigo.

Por suposto, Pandoc non ofrece conversión directa para cada par de formatos. Para soportar unha variedade tan ampla de transformacións, utilízase unha solución arquitectónica estándar: primeiro, todo o documento tradúcese nunha representación intermedia interna especial e, a continuación, xérase un documento nun formato diferente a partir desta representación interna. Os desenvolvedores chaman á representación interna "AST", que significa Árbore de sintaxe abstracta, ou árbore de sintaxe abstracta. Podes ver a representación intermedia de forma moi sinxela: todo o que tes que facer é configurar o formato de saída como "nativo"

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

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

Os lectores que traballaron con Haskell polo menos un pouco xa poden asumir a partir deste pequeno exemplo que Pandoc está escrito en Haskell: a saída deste comando é unha representación en cadea das estruturas internas de Pandoc, creada a semellanza de como se fai habitualmente. en Haskell, por exemplo, na biblioteca estándar.

Entón, aquí podes ver que a representación interna é unha estrutura recursiva, en cada nodo interno do cal hai unha lista. Por exemplo, no nivel superior hai unha lista dun elemento: a cabeceira do primeiro nivel cos atributos "hello-world",[],[]. Oculta dentro desta cabeceira unha lista da cadea "Ola", seguida dun espazo e a cadea "Mundo!".

Como podes ver, a representación interna non é moi diferente da HTML. É unha árbore onde cada nodo interno proporciona algunha información sobre o formato dos seus descendentes, e as follas conteñen o contido real do documento.

Se baixamos ao nivel de implementación, o tipo de datos para todo o documento defínese así:

data Pandoc = Pandoc Meta [Block]

Aquí Block son precisamente os vértices internos mencionados anteriormente, e Meta é metainformación sobre o documento, como o título, a data de creación, os autores; isto é diferente para os diferentes formatos, e Pandoc intenta, se é posible, conservar esa información ao traducir de formato a formato. formato.

Case todos os construtores do tipo Bloque (por exemplo, Cabeceira ou Para (parágrafo)) toman atributos e unha lista de vértices de nivel inferior como argumentos - En liña, por regra. Por exemplo, Space ou Str son construtores do tipo Inline, e a etiqueta HTML tamén se converte na súa propia Inline especial. Non vemos sentido proporcionar unha definición completa destes tipos, pero teña en conta que se pode atopar aquí aquí.

Curiosamente, o tipo Pandoc é un monoide. Isto significa que hai algún tipo de documento baleiro e que os documentos pódense apilar xuntos. Isto é cómodo de usar ao escribir Readers: pode dividir un documento en partes usando lóxica arbitraria, analizar cada unha por separado e, a continuación, xuntar todo nun só documento. Neste caso, a metainformación recollerase de todas as partes do documento á vez.

Ao converter, por exemplo, de LaTeX a HTML, primeiro un módulo especial chamado LaTeXReader converte o documento de entrada en AST, despois outro módulo chamado HTMLWriter converte o AST a HTML. Grazas a esta arquitectura, non hai necesidade de escribir un número cuadrático de conversións: é suficiente escribir Reader e Writer para cada novo formato e admitiranse automaticamente todos os pares posibles de conversións.

Está claro que unha arquitectura deste tipo tamén ten os seus inconvenientes, previstos dende hai tempo polos expertos no campo da arquitectura de software. O máis significativo é o custo de facer cambios na árbore de sintaxe. Se o cambio é o suficientemente grave, terás que cambiar o código en todos os lectores e escritores. Por exemplo, un dos retos aos que se enfrontan os desenvolvedores de Pandoc é admitir formatos de táboa complexos. Agora Pandoc só pode crear táboas moi sinxelas, cunha cabeceira, columnas e un valor en cada cela. Por exemplo, o atributo colspan en HTML simplemente ignorarase. Unha das razóns deste comportamento é a falta dun esquema unificado para representar táboas en todos ou polo menos en moitos formatos; polo tanto, non está claro en que forma deben almacenarse as táboas na representación interna. Pero mesmo despois de seleccionar unha vista específica, terás que cambiar absolutamente todos os lectores e escritores que admitan traballar con táboas.

A linguaxe Haskell foi escollida non só polo gran amor dos autores pola programación funcional. Haskell é coñecida polas súas amplas capacidades de procesamento de texto. Un exemplo é a biblioteca parsec é unha biblioteca que utiliza activamente os conceptos de programación funcional -monoides, mónadas, funtores aplicativos e alternativos- para escribir analizadores arbitrarios. Pódese ver todo o poder de Parsec en exemplo de HaskellWiki, onde se analiza un analizador completo dunha linguaxe de programación imperativa sinxela. Por suposto, Parsec tamén se usa activamente en Pandoc.

Brevemente descrito, as mónadas utilízanse para a análise secuencial, cando unha cousa aparece primeiro e despois outra. Por exemplo, neste exemplo:

whileParser :: Parser Stmt
whileParser = whiteSpace >> statement

Primeiro cómpre contar o espazo e despois a instrución, que tamén ten o tipo Parser Stmt.

Os funtores alternativos úsanse para retrotraer se a análise falla. Por exemplo,

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

Significa que ten que tentar ler a declaración entre corchetes ou tentar ler varias declaracións secuencialmente.

Os funtores aplicativos úsanse principalmente como atallos para as mónadas. Por exemplo, deixe que a función tok lea algún token (esta é unha función real de LaTeXReader). Vexamos esta combinación

const <$> tok <*> tok

Lerá dúas fichas seguidas e devolverá a primeira.

Para todas estas clases, Haskell ten fermosos operadores simbólicos, o que fai que a programación de Reader pareza arte ASCII. Só admira este marabilloso código.

As nosas tarefas estaban relacionadas con LaTeXReader. A tarefa de Vasily era soportar os comandos mbox e hbox, útiles para escribir paquetes en LaTeX. Elizabeth foi a responsable de dar soporte ao comando epigraph, que che permite crear epígrafes en documentos LaTeX.

Hatrace

Os sistemas operativos tipo UNIX adoitan implementar a chamada ao sistema ptrace. É útil para depurar e simular ambientes de programas, o que lle permite rastrexar as chamadas ao sistema que fai o programa. Por exemplo, a moi útil utilidade strace usa ptrace internamente.

Hatrace é unha biblioteca que proporciona unha interface para rastrexar en Haskell. O caso é que ptrace en si é moi sofisticado e é bastante difícil usalo directamente, sobre todo desde linguaxes funcionais.

Hatrace funciona como strace ao inicio e acepta argumentos similares. Diferénciase de strace en que tamén é unha biblioteca que proporciona unha interface máis sinxela que só ptrace.

Coa axuda de hatrace, xa detectamos un erro desagradable no compilador GHC Haskell: ao ser eliminado no momento equivocado, xera ficheiros de obxecto incorrectos e non os recompila cando se reinicia. A creación de secuencias de comandos mediante chamadas ao sistema permitiu reproducir de forma fiable o erro nunha soa execución, mentres que as eliminacións aleatorias reproducían o erro nunhas dúas horas.

Engadimos interfaces de chamadas do sistema á biblioteca: Elizaveta engadiu brk e Vasily engadiu mmap. En base aos resultados do noso traballo, é posible utilizar de forma máis sinxela e precisa os argumentos destas chamadas ao sistema cando se utiliza a biblioteca.

Fonte: www.habr.com

Engadir un comentario