\chapter{Introduzione a ML} \label{ML} Questo capitolo è una breve introduzione al meta-linguaggio \ML. L'obiettivo è solo quello di dare una sensazione di come sia interagire con il linguaggio. Si può trovare un'introduzione più dettagliata in numerosi libri di testo e pagine web; si veda per esempio l'elenco delle risorse sulla home page di MoscowML\footnote{\url{http://www.dina.kvl.dk/~sestoft/mosml.html}}, o le FAQ \texttt{comp.lang.ml}\footnote{\url{http://www.faqs.org/faqs/meta-lang-faq/}}. \section{Come interagire con ML} \ML{} è un linguaggio di programmazione interattivo come il Lisp. Al livello dell'interprete si possono valutare espressioni ed eseguire dichiarazioni. Le prime risultano nella stampa a schermo del valore dell'espressione e del suo tipo, le seconde nel binding di un valore a un nome. Un modo standard di interagire con \ML{} è quello di configurare lo schermo della workstation in modo tale che ci siano due finestre: \begin{myenumerate} \item Una finestra di modifica in cui i comandi \ML{} sono inizialmente battuti e registrati. \item Una finestra shell (o l'equivalente non-Unix) che è usata per valutare i comandi. \end{myenumerate} \noindent Un modo comune per ottenere questo è lavorare all'interno di \ml{Emacs} con una finestra di testo e una finestra shell. Dopo ave battuto un comando sulla finestra di modifica (testo) esso può essere trasferito alla shell e valutato in \HOL{} con un `copia e incolla'. In \ml{Emacs} questo si ottiene copiando il testo in un buffer e poi facendo il suo `yanking' nella shell. Il vantaggio di lavorare con un editor è che se il comando ha un errore, allora il testo può essere modificato semplicemente e usato di nuovo; inoltre esso salva i comandi in un file che può poi essere usato di nuovo più avanti (attraverso un caricamento batch). In \ml{Emacs}, la finestra di shell registra anche la sessione, inclusi sia l'input dell'utente sia la risposta del sistema. Le sessioni in questo tutorial sono state prodotte in questo modo. Queste sessioni sono divise in segmenti mostrati in riquadri con un numero sul loro angolo superiore destro (per indicare la loro posizione nella sessione completa). Le interazioni in questi riquadri dovrebbero essere intese operare in sequenza. Per esempio, si assume che i binding delle variabili fatte in box precedenti persistano nei successivi. Per entrare nel sistema \HOL{} si digita {\small\verb|hol|} o {\small\verb|hol.noquote|} in Unix, eventualmente preceduti dall'informazione del percorso se la directory bin del sistema \HOL{} non è nel proprio percorso. Il sistema \HOL{} quindi stampa un messaggio di sign-on e ci colloca all'interno di \ML. Il prompt \ML{} è {\small\verb|-|}, perciò le righe cominciano con {\small\verb|-|} sono quelle digitate dall'utente e le altre righe sono le risposte del sistema. Qui, come nelle altre parti del \TUTORIAL{}, assumeremo che si stia usando {\small\verb|hol|}. \setcounter{sessioncount}{0} \begin{session}\begin{alltt} \$ bin/hol ----------------------------------------------------------------- HOL-4 [\holnsversion (built Fri Apr 12 15:34:35 2002)] For introductory HOL help, type: help "hol"; ----------------------------------------------------------------- [loading theories and proof tools ************* ] [closing file "/local/scratch/mn200/Work/hol98/tools/end-init-boss.sml"] - 1 :: [2,3,4,5]; > val it = [1, 2, 3, 4, 5] : int list \end{alltt} \end{session} L'espressione \ML{} {\small\verb|1 :: [2,3,4,5]|} ha la forma $e_1\ op\ e_2$ dove $e_1$ è l'espressione {\small\verb|1|} (il cui valore è l'intero $1$), $e_2$ è l'espressione {\small\verb|[2,3,4,5]|} (il cui valore è una lista di quattro interi) e $op$ è l'operatore infisso `{\small\verb|::|}' che è come la funzione del Lisp {\it cons}. Altre funzioni di elaborazione di liste includono {\small\verb|hd|} ($car$ in Lisp), {\small\verb|tl|} ($cdr$ in Lisp) e {\small\verb|null|} ($null$ in Lisp). Il punto e virgola `{\small\verb|;|}' termina una frase complessiva. La risposta del sistema è mostrata sulla linea che comincia con il prompt {\small\verb|>|}. Essa consiste del valore dell'espressione seguito, dopo un segno di due punti, dal suo tipo. Il type checker di \ML{} deduce il tipo delle espressioni usando metodi inventati da Robin Milner \cite{Milner-types}. Il tipo {\small\verb|int list|} è il tipo delle `liste di interi'; {\small\verb|list|} è un operatore di tipo unario. Il sistema di tipi di \ML{} è molto simile al sistema di tipi della logica \HOL{} che è spiegata nel Capitolo~\ref{HOLlogic}. Il valore dell'ultima espressione valutata dal top level \ML{} è sempre memorizzata in una variabile chiamata {\small\verb|it|}. \begin{session} \begin{verbatim} - val l = it; > val l = [1, 2, 3, 4, 5] : int list - tl l; > val it = [2, 3, 4, 5] : int list - hd it; > val it = 2 : int - tl(tl(tl(tl(tl l)))); > val it = [] : int list \end{verbatim} \end{session} Seguendo gli standard del $\lambda$-calcolo, l'applicazione di una funzione $f$ a un argomento $x$ può essere scritta senza parentesi come $f\ x$ (benché è permesso anche il più convenzionale $f${\small\verb|(|}$x${\small\verb|)|}). L'espressione $f\ x_1\ x_2\ \cdots\ x_n$ abbrevia l'espressione meno intelligibile {\small\verb|(|}$\cdots${\small\verb|((|}$f\ x_1$% {\small\verb|)|}$x_2${\small\verb|)|}$\cdots${\small\verb|)|}$x_n$ (l'applicazione di funzione è associativa a sinistra). Le dichiarazioni hanno la forma {\small\verb|val |}$x_1${\small\verb|=|}$e_1${\small\verb| and |}$\cdots ${\small\verb| and |}$x_n${\small\verb|=|}$e_n$ e hanno l'effetto che il valore di ogni espressione $e_i$ venga legato al nome $x_i$. \begin{session} \begin{verbatim} - val l1 = [1,2,3] and l2 = ["a","b","c"]; > val l1 = [1, 2, 3] : int list val l2 = ["a", "b", "c"] : string list \end{verbatim} \end{session} Espressioni \ML{} come {\small\verb|"a"|}, {\small\verb|"b"|}, {\small\verb|"foo"|} \etc\ sono {\it stringhe\/} e hanno il tipo {\small\verb|string|}. Qualsiasi sequenza di caratteri {\small ASCII} può essere scritta tra virgolette\footnote{I caratteri di nuova riga devono essere scritti come \ml{$\backslash$n}, e le virgolette come \ml{$\backslash$"}.}. La funzione {\small\verb|explode|} divide una stringa in una lista di caratteri singoli, che sono scritti come stringhe di un singolo carattere, con anteposto un carattere {\small\verb|#|}. \begin{session} \begin{verbatim} - explode "a b c"; > val it = [#"a", #" ", #"b", #" ", #"c"] : char list \end{verbatim} \end{session} Un'espressione della forma {\small\verb|(|}$e_1${\small\verb|,|}$e_2${\small\verb|)|} viene valutata a una coppia dei valori di $e_1$ e $e_2$. Se $e_1$ ha il tipo $\sigma_1$ e $e_2$ ha il tipo $\sigma_2$ allora {\small\verb|(|}$e_1${\small\verb|,|}$e_2${\small\verb|)|} ha il tipo $\sigma_1${\small\verb|*|}$\sigma_2$. Il primo e il secondo componente di una coppia possono essere estratti con le funzioni \ML{} {\small\verb|#1|} e {\small\verb|#2|} rispettivamente. Se una tupla ha più di due componenti, il suo $n$-esimo componente può essere estratto con una funzione {\small\verb|#|$n$}. I valori {\small\verb|(1,2,3)|}, {\small\verb|(1,(2,3))|} e {\small\verb|((1,2), 3)|} sono tutti distinti e hanno i tipi \linebreak{} {\small\verb|int * int * int|}, {\small\verb|int * (int * int)|} and {\small\verb|(int * int) * int|} rispettivamente. \begin{session} \begin{verbatim} - val triple1 = (1,true,"abc"); > val triple1 = (1, true, "abc") : int * bool * string - #2 triple1; > val it = true : bool - val triple2 = (1, (true, "abc")); > val triple2 = (1, (true, "abc")) : int * (bool * string) - #2 triple2;; > val it = (true, "abc") : bool * string \end{verbatim} \end{session} \noindent L'espressioni \ML{} {\small\verb|true|} e {\small\verb|false|} denotano i due valori di verità di tipo {\small\verb|bool|}. I tipi \ML{} possono contenere {\it variabili di tipo\/} {\small\verb|'a|}, {\small\verb|'b|}, {\small\verb|'c|}, \etc\ Tali tipi sono chiamati {\it polimorfici\/}. Una funzione con un tipo polimorfico dovrebbe essere pensata come se possedesse tutti i tipi che si possono ottenere sostituendo le variabili di tipo con dei tipi. Questo è illustrato di sotto con la funzione {\small\verb|zip|}. Le funzioni sono definite con dichiarazioni della forma {\small\verb|fun|}$\ f\ v_1\ \ldots\ v_n$ \ml{=} $e$ dove ciascuna $v_i$ è o una variabile o un pattern composto di variabili. La funzione {\small\verb|zip|}, di sotto, converte una coppia di liste {\small\verb|([|}$x_1${\small\verb|,|}$\ldots${\small\verb|,|}$x_n$% {\small\verb|], [|}$y_1${\small\verb|,|}$\ldots${\small\verb|,|}$y_n$% {\small\verb|])|} in una lista di coppie {\small\verb|[(|}$x_1${\small\verb|,|}$y_1${\small\verb|),|}$\ldots$% {\small\verb|,(|}$x_n${\small\verb|,|}$y_n${\small\verb|)]|}. \begin{session} \begin{verbatim} - fun zip(l1,l2) = if null l1 orelse null l2 then [] else (hd l1,hd l2) :: zip(tl l1,tl l2); > val zip = fn : 'a list * 'b list -> ('a * 'b) list - zip([1,2,3],["a","b","c"]); > val it = [(1, "a"), (2, "b"), (3, "c")] : (int * string) list \end{verbatim} \end{session} Le funzioni possono essere {\it curried\/}, \ie\ prendono i lori argomenti `uno alla volta' al posto che come una tupla. Questo è illustrato con la funzione {\small\verb|curried_zip|} di sotto: \begin{session} \begin{verbatim} - fun curried_zip l1 l2 = zip(l1,l2); > val curried_zip = fn : 'a list -> 'b list -> ('a * 'b) list - fun zip_num l2 = curried_zip [0,1,2] l2; > val zip_num = fn : 'a list -> (int * 'a) list - zip_num ["a","b","c"]; > val it = [(0, "a"), (1, "b"), (2, "c")] : (int * string) list \end{verbatim} \end{session} La valutazione di un'espressione o {\it ha successo\/} o {\it fallisce\/}. Nel primo caso, la valutazione restituisce un valore; nel secondo caso la valutazione è interrotta ed è sollevata un'\emph{eccezione}. Questa eccezione è passata a qualunque cosa abbia invocato la valutazione. Questo contesto può o propagare il fallimento (questo è il default) o può {\it gestirlo\/}. Queste due possibilità sono illustrate di sotto. La gestione di un'eccezione è un'espressione della forma $e_1${\small\verb| handle _ => |}$e_2$. Un'espressione di questa forma è valutata valutando per prima cosa $e_1$. Se la valutazione ha successo (\ie\ non fallisce) allora il valore dell'intera espressione è il valore di $e_1$. Se la valutazione di $e_1$ ha sollevato un'eccezione, allora il valore dell'intera espressione è ottenuto valutando $e_2$.\footnote{Questa descrizione della gestione dell'eccezioni è di fatto una semplificazione grossolana del modo in cui le eccezioni possono essere gestite in \ML{}; si consulti un testo appropriato per una spiegazione migliore.}. \begin{session} \begin{verbatim} - 3 div 0; ! Uncaught exception: ! Div - 3 div 0 handle _ => 0; > val it = 0 : int \end{verbatim} \end{session} Le sessioni di sopra sono sufficienti per dare un idea dell'\ML. Nel prossimo capitolo, sarà introdotta la logica supportata dal sistema \HOL{} (la logica di ordine superiore), insieme con gli strumenti all'interno di \ML{} per manipolarla. %%% Local Variables: %%% mode: latex %%% TeX-master: "tutorial" %%% End: