A giugno di quest'anno, nella piccola città svizzera di Rapperswil, si è svolto per la decima volta un evento dal titolo . In questa occasione, sono accorsi oltre cinquecento appassionati di Haskell, dai neofiti ai pionieri del linguaggio. Sebbene gli organizzatori definiscano questo evento come un hackathon, non è una conferenza o un hackathon nel senso classico del termine. Il suo formato si discosta da quello tradizionale dei programmatori. Abbiamo scoperto ZuriHac per puro caso, abbiamo partecipato e ora riteniamo sia nostro dovere raccontare questa scoperta insolita!

Chi siamo
Questo articolo è stato redatto da due studenti del terzo anno del programma di "Matematica Applicata e Informatica" dell'Università Nazionale di Ricerca - Scuola Superiore di Economia di San Pietroburgo: Vasiliy Alferov e Yelizaveta Vasilenko. La nostra passione per la programmazione funzionale è iniziata entrambi con il ciclo di conferenze di D. N. Moskvin nel secondo anno dell'università. Attualmente, Vasiliy partecipa al programma Google Summer of Code, nell'ambito del quale lavora all'implementazione di grafi algebrici in Haskell sotto la supervisione del team del progetto . Yelizaveta ha applicato le competenze acquisite nella programmazione funzionale nel suo progetto di tesi, dedicato all'implementazione dell'algoritmo di anti-unificazione con successiva applicazione nella teoria dei tipi.
Formato dell'evento
Il pubblico target è costituito da proprietari di progetti open source, programmatori interessati a contribuire al loro sviluppo, ricercatori nella programmazione funzionale e semplici appassionati di Haskell. Quest'anno, presso l'università HSR Hochschule für Technik Rapperswil, si sono riuniti sviluppatori provenienti da più di cinquanta progetti open source in Haskell da tutto il mondo, per presentare i loro prodotti e attirare nuove persone per il loro sviluppo.

Foto da Twitter
Il processo è molto semplice: bisogna scrivere in anticipo alcune frasi sul proprio progetto e inviarle agli organizzatori, che pubblicheranno le informazioni sul progetto nella pagina dell'evento. Inoltre, nel primo giorno, gli autori dei progetti hanno a disposizione trenta secondi per raccontare brevemente dal palco di cosa si occupano e cosa c'è da fare. Successivamente, le persone interessate cercano gli autori e fanno domande dettagliate sui compiti.
Al momento non abbiamo progetti aperti di proprietà, ma desideriamo molto contribuire a quelli esistenti, quindi ci siamo registrati come partecipanti comuni. Nel giro di tre giorni abbiamo lavorato con due gruppi di sviluppatori. Risulta che lo studio collaborativo del codice e la comunicazione diretta rendono molto produttivo il rapporto tra gli autori del progetto e i contributori – a ZuriHac siamo riusciti a comprendere aree nuove per noi e siamo stati in grado di aiutare due team completamente diversi, risolvendo un compito in ciascuno dei progetti.
Oltre alla preziosa pratica, a ZuriHac sono state anche tenute diverse conferenze e laboratori. Due conferenze ci hanno colpito in particolare. Nella prima, Andrey Mokhov dell'Università di Newcastle ha parlato di funzioni applicative selettive – una classe di tipi che dovrebbe diventare intermedia tra le funzioni applicative e le monadi. Nell'altra conferenza, uno dei fondatori di Haskell, Simon Peyton Jones, ha discusso il funzionamento dell'inferenza di tipi nel compilatore GHC.

Conferenza di Simon Peyton Jones. Foto da Twitter
I laboratori tenuti durante l'hackathon erano suddivisi in tre categorie in base al livello di preparazione dei partecipanti. Anche i compiti proposti ai partecipanti che si univano allo sviluppo dei progetti avevano delle etichette con il livello di difficoltà. Una comunità numericamente esigua, ma affiatata, di programmatori funzionali accoglie volentieri i nuovi arrivati. Tuttavia, per comprendere le conferenze di Andrey Mokhov e Simon Peyton Jones, ci è stata molto utile la materia di programmazione funzionale che abbiamo seguito all'università.
La registrazione per l'evento è gratuita sia per i partecipanti comuni che per gli autori dei progetti. Abbiamo presentato le nostre candidature nei primi giorni di giugno, e dopo poco tempo siamo stati rapidamente trasferiti dalla lista d'attesa a quella dei partecipanti confermati.
E ora parleremo dei progetti a cui abbiamo partecipato nello sviluppo.
Pandoc
è un convertitore universale di documenti testuali, praticamente da qualsiasi formato a qualsiasi altro. Ad esempio, da docx a pdf, o da Markdown a MediaWiki. Il suo autore, John MacFarlane, è professore di filosofia all'Università della California a Berkeley. In generale, Pandoc è abbastanza conosciuto e alcuni dei nostri conoscenti sono rimasti sorpresi quando hanno scoperto che Pandoc è scritto in Haskell.

Elenco dei formati di documenti supportati da Pandoc. Sul sito è presente anche un grafico, ma questa immagine non può essere inserita nell'articolo.
Naturalmente, in Pandoc non è implementata una conversione diretta per ogni coppia di formati. Per supportare un così ampio numero di trasformazioni viene utilizzata una soluzione architetturale standard: prima l'intero documento viene tradotto in una rappresentazione intermedia speciale, e poi da questa rappresentazione intermedia viene generato un documento in un altro formato. La rappresentazione intermedia gli sviluppatori la chiamano «AST», che sta per Abstract Syntax Tree, o . È molto semplice visualizzare la rappresentazione intermedia: basta impostare come formato di uscita «native»
$ cat example.html
<h1>Ciao, Mondo!</h1>
$ pandoc -f html -t native example.html
[Header 1 ("hello-world",[],[]) [Str "Ciao,",Space,Str "Mondo!"]]
I lettori che hanno lavorato anche solo un po' con Haskell potrebbero già supporre da questo piccolo esempio che Pandoc sia scritto proprio in Haskell: l'output di questo comando è la rappresentazione delle strutture interne di Pandoc sotto forma di stringa, creata secondo come viene normalmente fatto in Haskell, ad esempio nella libreria standard.
Quindi, qui possiamo vedere che la rappresentazione intermedia è una struttura ricorsiva, in cui ogni nodo interno contiene un elenco. Ad esempio, al livello più alto c'è un elenco di un singolo elemento: un'intestazione di primo livello con attributi "hello-world",[],[]. All'interno di questa intestazione è nascosto un elenco della stringa "Hello", uno spazio e la stringa "World!".
Come si può vedere, la rappresentazione intermedia non è molto diversa dall'HTML. Essa rappresenta un albero, in cui ogni nodo interno comunica alcune informazioni sul formato dei propri discendenti, mentre nelle foglie si trova il contenuto del documento.
Se scendiamo al livello di una specifica implementazione, il tipo di dati per l'intero documento è definito così:
data Pandoc = Pandoc Meta [Block]Qui Block è proprio ciò di cui si parla sopra, mentre Meta è la meta-informazione riguardante il documento, come il titolo, la data di creazione, gli autori: per diversi formati è diversa, e Pandoc cerca di mantenere tali informazioni il più possibile durante la conversione da un formato all'altro.
Quasi tutti i costruttori di tipo Block — per esempio, Header o Para (paragrafo) — accettano come argomenti attributi e un elenco di nodi di livello inferiore — inline, di solito. Ad esempio, Space o Str sono costruttori di tipo Inline, mentre il tag HTML
viene trasformato nel suo speciale Inline. Non vediamo motivo di fornire una definizione completa di questi tipi, tuttavia notiamo che può essere consultata qui .
È interessante notare che il tipo Pandoc è un monoide. Questo significa che esiste un documento vuoto e che i documenti possono essere combinati tra loro. Questo è utile nella scrittura di Reader — è possibile suddividere un documento in parti secondo una logica arbitraria, analizzare ogni parte separatamente e poi assemblare tutto insieme in un unico documento. Nel processo, le metainformazioni verranno raccolte da tutte le parti del documento contemporaneamente.
Durante la conversione, ad esempio, da LaTeX a HTML, prima un modulo speciale chiamato LaTeXReader trasforma il documento di input in un AST, poi un altro modulo chiamato HTMLWriter trasforma l'AST in HTML. Grazie a tale architettura, non è necessario scrivere un numero quadratico di conversioni — è sufficiente scrivere un Reader e un Writer per ogni nuovo formato, e tutte le possibili coppie di conversione saranno automaticamente supportate.
È chiaro che tale architettura presenta anche i suoi svantaggi, da tempo previsti dagli esperti nel campo dell'architettura del software. Il più significativo è il costo delle modifiche nell'albero sintattico. Se la modifica è abbastanza sostanziale, sarà necessario cambiare il codice in tutti i Reader e Writer. Ad esempio, una delle sfide che i programmatori di Pandoc devono affrontare è il supporto di formati complessi per le tabelle. Attualmente, Pandoc gestisce solo tabelle molto semplici, con intestazioni, colonne e valori in ogni cella. Ad esempio, l'attributo colspan in HTML sarà semplicemente ignorato. Una delle ragioni di tale comportamento è l'assenza di uno schema unico di rappresentazione delle tabelle in tutti o almeno molti formati — di conseguenza, non è chiaro in quale forma sia necessario memorizzare le tabelle nella rappresentazione interna. Ma anche dopo aver scelto una rappresentazione specifica, sarà necessario modificare tutti i Reader e Writer che supportano il lavoro con le tabelle.
Il linguaggio Haskell è stato scelto non solo per il grande amore degli autori per la programmazione funzionale. Haskell è noto per le sue ampie possibilità di elaborazione del testo. Un esempio è la libreria — una libreria che utilizza attivamente i concetti della programmazione funzionale — monoidali, monadi, funttori applicativi e alternativi — per scrivere parser arbitrari. Tutta la potenza di Parsec può essere vista in HaskellWiki, dove viene analizzato un parser completo di un semplice linguaggio di programmazione imperativo. Naturalmente, Parsec è utilizzato anche in Pandoc.
Per descrivere brevemente, le monadi vengono utilizzate per il parsing sequenziale, dove prima viene un'operazione, e poi un'altra. Ad esempio, in questo esempio:
whileParser :: Parser Stmt
whileParser = whiteSpace >> statementPrima occorre leggere lo spazio bianco, e poi lo statement — che ha anch'esso il tipo Parser Stmt.
I funttori alternativi vengono utilizzati per tornare indietro nel caso in cui il parsing non riesca. Ad esempio,
statement :: Parser Stmt
statement = parens statement sequenceOfStmtQuesto significa che bisogna provare a leggere uno statement tra parentesi, oppure provare a leggere in sequenza più statement.
I funttori applicativi sono utilizzati principalmente come scorciatoie per le monadi. Ad esempio, supponiamo che la funzione tok legga un token (questa è una funzione reale di LaTeXReader). Diamo un'occhiata a questa combinazione
const <$ tok tokEssa leggerà due token consecutivi e restituirà il primo di essi.
Per tutte queste classi in Haskell esistono bellissimi operatori simbolici, rendendo la programmazione dei Reader simile ad un'arte ASCII. Basta ammirare questo codice straordinario.
I nostri compiti erano legati a LaTeXReader. Il compito di Vasiliy era supportare i comandi mbox e hbox, utili nella scrittura di pacchetti in LaTeX. La responsabilità di Elisabetta era il supporto del comando epigraph, che consente di formattare epigrafi nei documenti LaTeX.
Hatrace
Nei sistemi operativi simili a UNIX, spesso viene implementata la chiamata di sistema ptrace. È utile per il debug e la simulazione degli ambienti delle applicazioni, consentendo di monitorare le chiamate di sistema effettuate dal programma. Ad esempio, un'utile utility strace utilizza internamente proprio ptrace.
Hatrace è una libreria che fornisce un'interfaccia per ptrace in Haskell. Il punto è che il ptrace stesso è piuttosto complesso e utilizzarlo direttamente è piuttosto difficile, specialmente dai linguaggi funzionali.
Hatrace si comporta come strace all'avvio e accetta argomenti simili. La sua differenza rispetto a strace è che è anche una libreria che fornisce un'interfaccia più semplice rispetto a ptrace.
Con hatrace abbiamo già catturato un fastidioso bug nel compilatore Haskell GHC: se viene terminato in un momento inappropriato, genera file oggetto errati e non li ricompila al riavvio. Il tracciamento delle chiamate di sistema ha permesso di riprodurre l'errore in modo garantito con un singolo avvio, mentre le terminazioni casuali lo riproducevano in circa due ore.
Abbiamo aggiunto interfacce per le chiamate di sistema nella libreria: Elizaveta ha aggiunto brk e Vasily ha aggiunto mmap. Grazie al nostro lavoro, ora è possibile utilizzare gli argomenti di queste chiamate di sistema in modo più semplice e preciso quando si utilizza la libreria.
Fonte: habr.com
