From e662ac07fc60c43a38e2040a1c4374b96abd9bab Mon Sep 17 00:00:00 2001 From: indaco Date: Sun, 6 Sep 2015 22:48:48 +0200 Subject: [PATCH 1/2] Italian translation added Italian translation for the tutorial added. It is a good opportunity to review what I previously did in 2009 - http://bit.ly/1EJepIK --- it/tutorials/scala-for-java-programmers.md | 716 +++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 it/tutorials/scala-for-java-programmers.md diff --git a/it/tutorials/scala-for-java-programmers.md b/it/tutorials/scala-for-java-programmers.md new file mode 100644 index 0000000000..85135f73a6 --- /dev/null +++ b/it/tutorials/scala-for-java-programmers.md @@ -0,0 +1,716 @@ +--- +layout: overview +title: Un'introduzione a Scala per programmatori Java +overview: scala-for-java-programmers + +disqus: true +multilingual-overview: true +language: [it] +--- + +Di Michel Schinz e Philipp Haller. +Traduzione italiana a cura di Mirco Veltri. + +## Introduzione + +Lo scopo di questo documento è quello di fornire una rapida +introduzione al linguaggio e al compilatore Scala. È rivolto a +chi ha già qualche esperienza di programmazione e desidera una +panoramica di cosa è possibile fare con Scala. Si assume una +conoscenza di base dei concetti di programmazione orientata +agli oggetti, specialmente in Java. + +## Un Primo Esempio + +Come primo esempio useremo lo standard *Hello world*. Non è sicuramente +un esempio entusiasmante ma rende facile dimostrare l'uso dei tool di +Scala senza richiedere troppe conoscenze del linguaggio stesso. +Ecco come appeare il codice: + + object HelloWorld { + def main(args: Array[String]) { + println("Hello, world!") + } + } + +La struttura di questo programma dovrebbe essere familiare ai +programmatori Java: c'è un metodo chiamato `main` che accetta argomenti, +un array di stringhe, forniti da riga di comando come +parametri; Il corpo del metodo consiste di una singola chiamata al +predefinito `println` che riceve il nostro amichevole saluto +come parametro. Il metodo `main` non ritorna alcun valore +(è un metodo procedura), pertanto non è necessario dichiararne uno +di ritorno. + +Ciò che è meno familiare ai programmatori Java è la +dichiarazione di `object` contenente il metodo `main`. +Tale dichiarazione introduce ciò che è comunemente chiamato +*oggetto singleton*, cioè una classe con una unica istanza. +La dichiarazione precedente infatti crea sia la classe `HelloWorld` +che una istanza di essa, chiamata `HelloWorld`. L'istanza è creata +su richiesta la prima volta che è usata. + + +Il lettore astuto avrà notato che il metodo `main` non è stato +dichiarato come `static`. Questo perchè i membri (metodi o campi) +statici non esistono in Scala. Invece che definire membri statici, +il programmatore Scala li dichiara in oggetti singleton. + +### Compiliamo l'esempio + +Per compilare l'esempio, useremo `scalac`, cioè il compilatore Scala. +`scalac` lavora come la maggior parte dei compilatori: prende un file +sorgente come argomento, eventuali opzioni e produce uno o più object +file come output. Gli object file sono gli standard file delle classi +di Java. + +Se salviamo il file precedente come `HelloWorld.scala`, lo compiliamo +con il seguente comando (il segno maggiore `>' rappresenta il prompt +dei comandi e non va digitato): + + > scalac HelloWorld.scala + +sarà generato qualche class file nella directory corrente. Uno di questi +avrà il nome `HelloWorld.class` e conterrà una classe che può essere +direttamente eseguita con il comando `scala`, come mostra la seguente +sezione. + +### Eseguimo l'esempio + +Una volta compilato il programma pu\`o esser facilmente eseguito con il +comando scala. L'uso è molto simile al comando java ed accetta le stesse +opzioni. Il precedente esempio può esser eseguito usando il seguente +comando che produce l'output atteso: + + > scala -classpath . HelloWorld + + Hello, world! + +## Interazione con Java + +Uno dei punti di forza di è quello di rendere semplice l’interazione con +codice Java. Tutte le classi del package `java.lang` vengono importate di +default, le altre richiedono l’esplicito import. + +Osserviamo un esempio che lo dimostra. Vogliamo ottenere la data +corrente e formattarla in accordo con la convezione usata in uno +specifico paese del mondo, diciamo la Francia. (Altre regioni, come la parte +di lingua francese della Svizzera utilizzano le stesse convenzioni.) + +Le librerie delle classi Java definiscoono potenti classi di utilità, come +`Date` e `DateFormat`. Poiché Scala interagisce direttamente con Java, non +esistono le classi equivalenti nella libreria delle classi di Scala--possiamo +semplicemente importare le classi dei corrispondenti package Java: + + import java.util.{Date, Locale} + import java.text.DateFormat + import java.text.DateFormat._ + + object FrenchDate { + def main(args: Array[String]) { + val now = new Date + val df = getDateInstance(LONG, Locale.FRANCE) + println(df format now) + } + } + +L’istruzione import di Scala è molto simile all’equivalente in Java, +tuttavia, risulta essere più potente. Più classi possono essere importate +dallo stesso package includendole in parentesi graffe come nella prima linea +di codice precedentemente riportato. Un’altra differenza è evidente +nell’uso del carattere underscore (`_`) al posto dell’asterisco (`*`) per +importare tutti i nomi di un package o di una classe. Questo perché +l’asterisco è un identificatore valido (e.g. nome di un metodo), come +vedremo più avanti. + +Inoltre, l’istruzione import sulla terza linea importa tutti i membri +della classe `DateFormat`. Questo rende disponibili il metodo statico +`getDateInstance` ed il campo statico `LONG`. + +All’interno del metodo `main` creiamo un’istanza della classe `Date` di +Java che di default contiene la data corrente. Successivamente, definiamo il +formato della data usando il metodo statico `getDateInstance` importato +precedentemente. Infine, stampiamo la data corrente, formattata secondo la +localizzazione scelta, con l’istanza `DateFormat`; quest’ultima linea mostra +un’importante proprietà di Scala.I metodi che prendono un argomento possono +essere usati con una sintassi non fissa. Questa forma dell’espressione + + df format now + +è solo un altro modo meno esteso di scriverla come + + df.format(now) + +Apparentemente sembra un piccolo dettaglio sintattico ma, presenta delle +importanti conseguenze. Una di queste sarà esplorata nella prossima +sezione. + +A questo punto, riguardo l’integrazione con Java abbiamo notato che è +altresì possibile ereditare dalle classi Java ed implementare le interfacce +direttamente in Scala. + + +## Tutto è un Oggetto + +Scala è un linguaggio orientato agli oggetti (_object-oriented_) puro nel +senso che *ogni cosa* è un oggetto, inclusi i numeri e le funzioni. In questo +differisce da Java che invece distingue tra tipi primitivi (come `boolean` + e `int` ) e tipi referenziati. I\noltre, Java non permette la manipolazione + di funzioni come fossero valori. + +### I numeri sono oggetti + +Poichè i numeri sono oggetti, hanno dei metodi. Di fatti +un’espressione aritmetica come la seguente: + + 1 + 2 * 3 / x + +consiste esclusivamente di chiamate a metodi e risulta equivalente alla +seguente espressione, come visto nella sezione precedente: + + (1).+(((2).*(3))./(x)) + +Questo significa anche che `+`, `*`, etc. sono identificatori validi in +in Scala. + +Le parentesi intorno ai numeri nella seconda versione sono necessarie +perché l’analizzatore lessicale di Scala usa le regole di match più +lunghe per i token quindi, dovrebbe dividere la seguente espressione: + + 1.+(2) + +nei token `1.`, `+`, and `2`. La ragione per cui si è scelto questo tipo +di assegnazione di significato è perché `1.` è un match più lungo e valido +di `1`. Il token `1.` è interpretato come `1.0` rendendolo un `Double` e +non più un `Int`. Scrivendo l’espressione come: + + (1).+(2) + +si evita che `1` sia interpretato come un `Double`. + +### Le funzioni sono oggetti + +Forse per i programmatori Java è più sorprendente scoprire che in Scala +anche le funzioni sono oggetti. È pertanto possibile passare le funzioni +come argomenti, memorizzarle in variabili e ritornarle da altre funzioni. +L’abilità di manipolare le funzioni come valori è uno dei punti +cardini di un interessante paradigma di programmazione chiamato +*programmazione funzionale*. + +Come esempio semplice del perché può risultare utile usare le funzioni +come valori consideriamo una funzione timer che deve eseguire delle +azione ogni secondo. Come specifichiamo l’azione da eseguire? +Logicamente, come una funzione. Questo tipo di passaggio di funzione è +familiare a molti programmatori: viene spesso usato nel codice delle +interfacce utente, per registrare le funzioni di call-back richiamate +quando un evento si verifica. + +Nel successivo programma la funzione timer è chiamata `oncePerSecond` e +prende come argomento una funzione di call-back. Il tipo di questa +funzione è scritto come `() => Unit` che è il tipo di tutte le funzioni +che non prendono nessun argomento e non restituiscono niente (il tipo + `Unit` è simile al `void` del C/C++). La funzione principale di questo +programma è quella di chiamare la funzione timer con una call-back che +stampa una frase sul terminale. In altre parole questo programma stampa la +frase “time flies like an arrow” ogni secondo. + + object Timer { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def timeFlies() { + println("time flies like an arrow...") + } + def main(args: Array[String]) { + oncePerSecond(timeFlies) + } + } + +Notare che per stampare la stringa, usiamo il metodo `println` predefinito +invece di quelli inclusi in `System.out`. + +#### Funzioni anonime + +Il codice precedente è semplice da capire e possiamo raffinarlo ancora +un po’. Notiamo preliminarmente che la funzione `timeFlies` è definita +solo per esser passata come argomento alla funzione `oncePerSecond`. +Nominare esplicitamente una funzione con queste caratteristiche non è +necessario. È più interessante costruire detta funzione nel momento in +cui viene passata come argomento a `oncePerSecond`. Questo è possibile +in Scala usando le *funzioni anonime*, funzioni cioè senza nome. La +versione rivista del nostro programma timer usa una funzione anonima +invece di *timeFlies* e appare come di seguito: + + object TimerAnonymous { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def main(args: Array[String]) { + oncePerSecond(() => + println("time flies like an arrow...")) + } + } + +La presenza delle funzioni anonime in questo esempio è rivelata dal +simbolo `=>` che separa la lista degli argomenti della funzione dal +suo corpo. In questo esempio la lista degli argomenti è vuota e di fatti, +la coppia di parentesi sulla sinistra della freccia è vuota. Il corpo della +funzione `timeFlies` è lo stesso del precedente. + +## Le Classi + +Come visto precedentemente Scala è un linguaggio orientato agli oggetti e +come tale presenta il concetto di classe. (Per ragioni di completezza +va notato che alcuni linguaggi orientati agli oggetti non hanno il concetto +di classe ma, Scala non è uno di questi.) Le classi in Scala sono dichiarate +usando una sintassi molto simile a quella usata in Java. Un'importante +differenza è che le classi in Scala possono avere dei parametri. Questo +concetto è mostrato nella seguente definizione dei numeri complessi. + + class Complex(real: Double, imaginary: Double) { + def re() = real + def im() = imaginary + } + +Questa classe per i numeri complessi prende due argomenti, la parte +immaginaria e quella reale del numero complesso. Questi possono esser +passati quando si crea una istanza della classe `Complex` nel seguente +modo: `new Complex(1.5, 2.3)`. La classe ha due metodi, `re` e `im` che +danno l’accesso rispettivamente alla parte reale e a quella immaginaria +del numero complesso. + +Da notare che il tipo di ritorno dei due metodi non è specificato esplicitamante. +Sarà il compilatore che lo dedurrà automaticamente osservando la parte a destra +del segno uguale dei metodi e deducendo che per entrambi si tratta di +valori di tipo `Double`. + +Il compilatore non è sempre capace di dedurre i tipi come nel caso precedente; +purtroppo non c’è una regola semplice capace di dirci quando sarà in grado e +quando no. Nella pratica questo non è un problema poiché il compilatore sa quando +non è in grado di stabilire il tipo che non è stato definito +esplicitamente. Come semplice regola i programmatori Scala alle prime armi +dovrebbero provare ad omettere la dichiarazione di tipi che sembrano semplici +da dedurre per osservare il comportamento del compilatore. Dopo qualche tempo il +programmatore avrà la sensazione di quando è possibile omettere il tipo +e quando no. + +### Metodi senza argomenti + +Un piccolo problema dei metodi `re` e `im` è che, per essere invocati, è +necessario far seguire il nome del metodo da una coppia di parentesi tonde +vuote, come mostrato di seguito: + + object ComplexNumbers { + def main(args: Array[String]) { + val c = new Complex(1.2, 3.4) + println("imaginary part: " + c.im()) + } + } + +Sarebbe decisamente meglio riuscire ad accedere alla parte reale ed immaginaria +come se fossero campi senza dover scrivere anche la coppia vuota di parentesi. +Questo è perfettamente fattibile in Scala semplicemente definendo i relativi +metodi *senza argomenti*. Tali metodi differiscono da quelli con zero argomenti +perché non presentano la coppia di parentesi dopo il nome nè nella loro +definizione, nè nel loro utilizzo. La nostra classe `Complex` può essere +riscritta come segue: + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + } + + +### Eredità e overriding + +In Scala tutte le classi sono figlie di una super-classe. Quando nessuna +super-classe viene specificata, come nell’esempio della classe `Complex`, +la classe `scala.AnyRef` è implicitamente usata. + +In Scala è possibile eseguire la sovrascrittura (_override_) dei metodi +ereditati dalla super-classe. È pertanto necessario specificare esplicitamente +il metodo che si sta sovrascrivendo usando il modificatore `override`, al fine +di evitare sovrascritture accidentali. Come esempio estendiamo la nostra classe + `Complex` ridefinendo il metodo `toString` ereditato da `Object`. + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + override def toString() = + "" + re + (if (im < 0) "" else "+") + im + "i" + } + + +## Case Classes e Pattern Matching + +Un tipo di struttura dati che spesso si trova nei programmi è l’albero. +Ad esempio, gli interpreti ed i compilatori abitualmente rappresentano i +programmi internamente come alberi. I documenti XML sono alberi e +diversi tipi di contenitori sono basati sugli alberi, come gli alberi +red-black. + +Esamineremo ora come gli alberi sono rappresentati e manipolati in Scala +attraverso un semplice programma calcolatrice. Lo scopo del programma è +manipolare espressioni aritmetiche molto semplici composte da somme, +costanti intere e variabili intere. Due esempi di tali espressioni sono +`1+2` e `(x+x)+(7+y)`. + +A questo punto è necessario definire il tipo di rappresentazione per +dette espressioni e, a tale proposito, l’albero è la più naturale, dove +i nodi sono le operazioni (nel nostro caso, l’addizione) e le foglie +sono i valori (costanti o variabili). + +In questo albero è abitualmente rappresentato usando una super-classe +astratta per gli alberi e una concreta sotto-classe per i nodi o le +foglie. In un linguaggio funzionale useremmo un tipo dati algebrico per +lo stesso scopo. Scala fornisce il concetto di _case classes_ che è qualcosa +che si trova nel mezzo delle due rappresentazioni. Mostriamo come può essere +usato per definire il tipo di alberi per il nostro esempio: + + abstract class Tree + case class Sum(l: Tree, r: Tree) extends Tree + case class Var(n: String) extends Tree + case class Const(v: Int) extends Tree + +Il fatto che le classi `Sum`, `Var` e `Const` sono dichiarate come case classes +significa che rispetto alle classi standard differiscono in diversi aspetti: + +- la parola chiave `new` non è necessaria per creare un’istanza di queste + classi (i.e si può scrivere `Const(5)` invece di `new Const(5)`), +- le funzioni getter sono automaticamente definite per i parametri del + costruttore (i.e. è possibile ricavare il valore del parametro `v` del + costruttore di qualche istanza della classe `c` semplicemente + scrivendo `c.v`), +- sono disponibili le definizioni di default dei metodi `equals` e + `hashCode` che lavorano sulle *strutture* delle istanze e non sulle + loro identità, +- è disponibile la definizione di default del metodo `toString` che stampa + il valore in “source form” (e.g. l’albero per l’espressione `x+1` stampa + `Sum(Var(x),Const(1))`), +- le istanze di queste classi possono essere decomposte con il + *pattern matching* come vedremo più avanti. + +Ora che abbiamo definito il tipo dati per rappresentare le nostre +espressioni aritmetiche possiamo iniziare a definire le operazioni per +manipolarle. Iniziamo con una funzione per valutare l’espressione in un +qualche *ambiente* (_environment_) di valutazione. Lo scopo dell’environment +è quello di dare i valori alle variabili. Per esempio, l’espressione +`x+1` valutata nell’environment con associato il valore `5` alla +variabile `x`, scritto `{ x -> 5 }`, restituisce `6` come risultato. + +Inoltre dobbiamo trovare un modo per rappresentare gli environment. +Potremmo naturalmente usare alcune strutture dati associative come una +hash table ma, possiamo anche usare direttamente delle funzioni! Un +environment in realtà non è altro che una funzione con associato un +valore al nome di una variabile. L’environment `{ x -> 5 }` +mostrato sopra può essere semplicemente scritto in Scala come: + + { case "x" => 5 } + +Questa notazione definisce una funzione che quando riceve la stringa `"x"` +come argomento restituisce l’intero `5` e fallisce con un’eccezione negli +altri casi. + +Prima di scrivere la funzione di valutazione diamo un nome al tipo di +environment. Potremmo usare sempre il tipo `String => Int` per gli environment +ma, semplifichiamo il programma se introduciamo un nome per questo tipo +rendendo anche i cambiamenti futuri più facili. Questo è fatto in con la +seguente notazione: + + type Environment = String => Int + +Da ora in avanti il tipo `Environment` può essere usato come un alias per +il tipo delle funzioni da `String` a `Int`. + +Possiamo ora passare alla definizione della funzione di valutazione. +Concettualmente è molto semplice: il valore della somma di due +espressioni è pari alla somma dei valori delle loro espressioni; il +valore di una variabile è ottenuto direttamente dall’environment; il +valore di una costante è la costante stessa. Esprimere quanto appena +detto in Scala non è difficile: + + def eval(t: Tree, env: Environment): Int = t match { + case Sum(l, r) => eval(l, env) + eval(r, env) + case Var(n) => env(n) + case Const(v) => v + } + +Questa funzione di valutazione lavora effettuando un *pattern matching* +sull’albero `t`. Intuitivamente il significato della definizione precendente +dovrebbe esser chiaro: + +1. prima controlla se l’albero `t` è un `Sum`; se lo è, esegue il bind del + sottoalbero sinistro con una nuova variabile chiamata `l` ed il sotto + albero destro con una variabile chiamata `r` e procede con la + valutazione dell’espressione che segue la freccia; questa + espressione può (e lo fa) utilizzare le variabili marcate dal pattern che + appaiono alla sinistra della freccia, i.e. `l` e `r`; +2. se il primo controllo non è andato a buon fine, cioè l’albero non è + un `Sum`, va avanti e controlla se `t` è un `Var`; se lo è, esegue il bind + del nome contenuto nel nodo `Var` con una variabile `n` e procede con la + valutazione dell’espressione sulla destra; +3. se anche il secondo controllo fallisce e quindi `t` non è nè `Sum` nè `Var`, + controlla se si tratta di un `Const` e se lo è, combina il valore contenuto + nel nodo `Const` con una variabile `v` e procede con la valutazione + dell’espressione sulla destra; +4. infine, se tutti i controlli falliscono, viene sollevata + un’eccezione per segnalare il fallimento del pattern matching + dell’espressione; questo caso può accadere qui solo se si + dichiarasse almeno una sotto classe di `Tree`. + +L’idea alla base del pattern matching è quella di eseguire il match di +un valore con una serie di pattern e, non appena il match è trovato, estrarre +e nominare varie parti del valore per valutare il codice che ne fa uso. + +Un programmatore object-oriented esperto potrebbe sorprendersi del fatto +che non abbiamo definito `eval` come *metodo* della classe e delle sue +sottoclassi. Potremmo averlo fatto, perchè Scala permette la definizione di +metodi nelle case classes così come nelle classi normali. Decidere quando +usare il pattern matching o i metodi è quindi una questione di gusti ma, +ha anche implicazioni importanti riguardo l’estensibilità: + +- quando si usano i metodi è facile aggiungere un nuovo tipo di nodo + definendo una sotto classe di `Tree` per esso; d’altro canto, aggiungere + una nuova operazione per manipolare l’albero è noioso, richiede la + modifica di tutte le sotto classi `Tree`; +- quando si usa il pattern matching la situazione è ribaltata: + aggiungere un nuovo tipo di nodo richiede la modifica di tutte le + funzioni in cui si fa pattern matching sull’albero, per prendere in + esame il nuovo nodo; d’altro canto, aggiungere una nuova operazione + è semplice, basta definirla come una funzione indipendente. + +Per esplorare ulteriormente il pattern matching definiamo un’altra +operazione sulle espressioni aritmetiche: la derivazione simbolica. È +necessario ricordare le seguenti regole che riguardano questa +operazione: + +1. la derivata di una somma è la somma delle derivate, +2. la derivata di una variabile `v` è uno se `v` è la variabile di + derivazione, zero altrimenti, +3. la derivata di una costante è zero. + +Queste regole possono essere tradotte quasi letteralmente in codice Scala, +per ottenere la seguente definizione: + + def derive(t: Tree, v: String): Tree = t match { + case Sum(l, r) => Sum(derive(l, v), derive(r, v)) + case Var(n) if (v == n) => Const(1) + case _ => Const(0) + } + +Questa funzione introduce due nuovi concetti relativi al pattern +matching. Prima di tutto l’istruzione `case` per le variabili ha un +*controllo*, un’espressione che segue la parola chiave `if`. Questo +controllo fa si che il pattern matching è eseguito solo se l’espressione +è vera. Qui viene usato per esser sicuri che restituiamo la costante `1` +solo se il nome della variabile da derivare è lo stesso della variabile +di derivazione `v`. La seconda nuova feature del pattern matching qui +introdotta è la *wild-card*, scritta `_`, che corrisponde a qualunque +valore, senza assegnargli un nome. + +Non abbiamo esplorato del tutto la potenza del pattern matching ma, ci +fermiamo qui per brevità. Vogliamo ancora osservare come le due +precedenti funzioni lavorano in un esempio reale. A tale scopo, +scriviamo una semplice funzione `main` che esegue diverse operazioni +sull’espressione `(x+x)+(7+y)`: prima calcola il suo valore +nell’environment `{ x -> 5, y -> 7 }`, dopo calcola la +derivata relativa a `x` e poi ad `y`. + + def main(args: Array[String]) { + val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) + val env: Environment = { case "x" => 5 case "y" => 7 } + println("Expression: " + exp) + println("Evaluation with x=5, y=7: " + eval(exp, env)) + println("Derivative relative to x:\n " + derive(exp, "x")) + println("Derivative relative to y:\n " + derive(exp, "y")) + } + +Eseguendo questo programma, otteniamo l’output atteso: + + Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) + Evaluation with x=5, y=7: 24 + Derivative relative a x: + Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) + Derivative relative to y: + Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) + +Esaminando l’output, notiamo che il risultato della derivata dovrebbe +essere semplificato prima di essere visualizzato all’utente. La +definizione di una funzione di semplificazione usando il pattern +matching rappresenta un interessante (ma sorprendentemente +ingannevole) problema che lasciamo come esercizio per il lettore. + + +## I Trait + +Una classe in Scala oltre che poter ereditare da una super-classe può anche +importare del codice da uno o più *trait*. + +Probabilmente per i programmatori Java il modo più semplice per capire cosa +sono i trait è concepirli come interfacce che possono contenere del codice. +In Scala quando una classe eredita da un trait ne implementa la relativa +interfaccia ed eredita tutto il codice contenuto in essa. + +Per comprendere a pieno l’utilità dei trait osserviamo un classico +esempio: gli oggetti ordinati. Si rivela spesso utile riuscire a confrontare +oggetti di una data classe con se stessi, ad esempio per ordinarli. In Java +gli oggetti confrontabili implementano l’interfaccia `Comparable`. In Scala +possiamo fare qualcosa di meglio che in Java definendo l’equivalente codice +di `Comparable` come un trait, che chiamiamo `Ord`. + +Sei differenti predicati possono essere utili per confrontare gli +oggetti: minore, minore o uguale, uguale, diverso, maggiore e maggiore o uguale. + Tuttavia definirli tutti è noioso, specialmente perché 4 di +essi sono esprimibili con gli altri due. Per esempio, dati i predicati di +uguale e minore, è possibile esprimere gli altri. In Scala tutte queste +osservazioni possono essere piacevolemente inclusi nella seguente +dichiarazione di un trait: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +Questa definizione crea un nuovo tipo chiamato `Ord` che ha lo stesso +ruolo dell’interfaccia `Comparable` in Java e, fornisce l’implementazione +di default di tre predicati in termini del quarto astraendone uno. +I predicati di uguaglianza e disuguaglianza non sono presenti in questa +dichiarazione poichè sono presenti di default in tutti gli oggetti. + +Il tipo `Any` usato precedentemente è il super-tipo dati di tutti gli +altri tipi in Scala. Può esser visto come una versione generica del +tipo `Object` in Java dato che è altresì il super-tipo dei tipi base come +`Int`, `Float` ecc. + +Per rendere confrontabili gli oggetti di una classe è quindi sufficiente +definire i predicati con cui testare uguaglianza ed minoranza e unire la +precedente classe `Ord`. Come esempio, definiamo una classe `Date` che +rappresenta le date nel calendario Gregoriano. Tali date sono composte dal +giorno, dal mese e dall’anno che rappresenteremo tutti con interi. Iniziamo +definendo la classe `Date` come segue: + + class Date(y: Int, m: Int, d: Int) extends Ord { + def year = y + def month = m + def day = d + override def toString(): String = year + "-" + month + "-" + day + +La parte importante qui è la dichiarazione `extends Ord` che segue il nome +della classe e dei parametri. Dichiara che la classe `Date` eredita il +codice dal trait `extends Ord`. + +Successivamente ridefiniamo il metodo `equals`, ereditato da `Object`, +in modo tale che possa confrontare in modo corretto le date confrontando +i singoli campi. L’implementazione di default del metodo `equals` non è +utilizzabile perché, come in Java, confronta fisicamente gli oggetti. +Arriviamo alla seguente definizione: + + override def equals(that: Any): Boolean = + that.isInstanceOf[Date] && { + val o = that.asInstanceOf[Date] + o.day == day && o.month == month && o.year == year + } + +Questo metofo fa uso di due metodi predefiniti `isInstanceOf` e `asInstanceOf`. +Il primo, `isInstanceOf`, corrisponde all’operatore `instanceOf` di Java e +restituisce true se e solo se l’oggetto su cui è applicato è una istanza del +tipo dati. Il secondo, `asInstanceOf`, corrisponde all’operatore di cast in +Java: se l’oggetto è una istanza del tipo dati è visto come tale altrimenti +viene sollevata un’eccezione `ClassCastException`. + +L’ultimo metodo da definire è il predicato che testa la condizione di +minoranza. Fa uso di un altro metodo predefinito, `error`, che solleva +un'eccezione con il messaggio di errore specificato. + + def <(that: Any): Boolean = { + if (!that.isInstanceOf[Date]) + error("cannot compare " + that + " and a Date") + + val o = that.asInstanceOf[Date] + (year < o.year) || + (year == o.year && (month < o.month || + (month == o.month && day < o.day))) + } + +Questo completa la definizione della classe `Date`. Istanze di questa classe +possono esser viste sia come date che come oggetti confrontabili. +Inoltre, tutti e sei i predicati di confronto menzionati precedentemente +sono definiti: `equals` e `<` perché appaiono direttamente nella definizione +della classe `Date` e gli altri perché sono ereditati dal trait `Ord`. + +I trait naturalmente sono utili in molte situazioni più interessanti di quella +qui mostrata, ma la discussione delle loro applicazioni è fuori dallo scopo di +questo documento. + +## Programmazione Generica + +L’ultima caratteristica di Scala che esploreremo in questo tutorial è la +programmazione generica. Gli sviluppatori Java dovrebbero essere bene +informati dei problemi relativi alla mancanza della programmazione +generica nel loro linguaggio, un’imperfezione risolta in Java 1.5. + +La programmazione generica riguarda la capacità di scrivere codice +parametrizzato dai tipi. Per esempio, un programmatore che scrive una +libreria per le liste concatenate può incontrare il problema di decidere +quale tipo dare agli elementi della lista. Dato che questa lista è stata +concepita per essere usata in contesti differenti, non è possibile +decidere che il tipo degli elementi deve essere, per esempio, `Int`. +Questo potrebbe essere completamente arbitrario ed eccessivamente +restrittivo. + +I programmatori Java hanno fatto ricorso all’uso di `Object`, che è il +super-tipo di tutti gli oggetti. Questa soluzione è in ogni caso ben lontana +dall’esser ideale, poiché non funziona per i tipi base (`int`, `long`, `float`, +ecc.) ed implica che molto type casts dinamico deve esser fatto dal +programmatore. + +Scala rende possibile la definizione delle classi generiche (e metodi) per +risolvere tale problema. Esaminiamo ciò con un esempio del più semplice +container di classe possibile: un riferimento, che può essere o vuoto o +un puntamento ad un oggetto di qualche tipo. + + class Reference[T] { + private var contents: T = _ + def set(value: T) { contents = value } + def get: T = contents + } + +La classe `Reference` è parametrizzata da un tipo, chiamato `T`, che è il tipo +del suo elemento. Questo tipo è usato nel corpo della classe come il tipo della +variabile `contents`, l’argomento del metodo , ed il tipo restituito dal metodo +`get`. + +Il precedente codice d’esempio introduce le variabili in Scala che non +dovrebbero richiedere ulteriori spiegazioni. È tuttavia interessante +notare che il valore iniziale dato a quella variabile è `_`, che +rappresenta un valore di default. Questo valore di default è 0 per i +tipi numerici, `false` per il tipo `Boolean`, `())`per il tipo `Unit` +e `null` per tutti i tipi oggetto. + +Per usare la classe `Reference` è necessario specificare quale tipo usare per +il tipo parametro `T`, il tipo di elemento contenuto dalla cella. Adr esempio, +per creare ed usare una cella che contiene un intero, si potrebbe scrivere il +seguente codice: + + object IntegerReference { + def main(args: Array[String]) { + val cell = new Reference[Int] + cell.set(13) + println("Reference contains the half of " + (cell.get * 2)) + } + } + +Come si può vedere in questo esempio non è necessario il cast del +tipo ritornato dal metodo `get` prima di usarlo come intero. Non risulta +possibile memorizzare niente di diverso da un intero nella varibile +poiché è stata dichiarata per memorizzare un intero. + +## Conclusioni + +Questo documento ha fornito una veloce introduzione del linguaggio Scala e +presentato alcuni esempi di base. Il lettore interessato può continuare, per +esempio, leggendo il documento *Scala By Example* che contiene esempi molti più +avanzati e, consultare al bisogno la documentazione +*Scala Language Specification*. From 5afd6f523f51d34d8656421a6d448e173109f403 Mon Sep 17 00:00:00 2001 From: indaco Date: Fri, 11 Sep 2015 22:24:49 +0200 Subject: [PATCH 2/2] typos --- it/tutorials/scala-for-java-programmers.md | 114 ++++++++++----------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/it/tutorials/scala-for-java-programmers.md b/it/tutorials/scala-for-java-programmers.md index 85135f73a6..81248f98a2 100644 --- a/it/tutorials/scala-for-java-programmers.md +++ b/it/tutorials/scala-for-java-programmers.md @@ -58,13 +58,13 @@ il programmatore Scala li dichiara in oggetti singleton. ### Compiliamo l'esempio -Per compilare l'esempio, useremo `scalac`, cioè il compilatore Scala. +Per compilare l'esempio useremo `scalac`, il compilatore Scala. `scalac` lavora come la maggior parte dei compilatori: prende un file sorgente come argomento, eventuali opzioni e produce uno o più object file come output. Gli object file sono gli standard file delle classi di Java. -Se salviamo il file precedente come `HelloWorld.scala`, lo compiliamo +Se salviamo il file precedente come `HelloWorld.scala` e lo compiliamo con il seguente comando (il segno maggiore `>' rappresenta il prompt dei comandi e non va digitato): @@ -77,10 +77,10 @@ sezione. ### Eseguimo l'esempio -Una volta compilato il programma pu\`o esser facilmente eseguito con il +Una volta compilato il programma può esser facilmente eseguito con il comando scala. L'uso è molto simile al comando java ed accetta le stesse opzioni. Il precedente esempio può esser eseguito usando il seguente -comando che produce l'output atteso: +comando. L'output prodotto è quello atteso: > scala -classpath . HelloWorld @@ -88,16 +88,16 @@ comando che produce l'output atteso: ## Interazione con Java -Uno dei punti di forza di è quello di rendere semplice l’interazione con -codice Java. Tutte le classi del package `java.lang` vengono importate di -default, le altre richiedono l’esplicito import. +Uno dei punti di forza di Scala è quello di rendere semplice l’interazione con +codice Java. Tutte le classi del package `java.lang` sono importate di +default mentre le altre richiedono l’esplicito import. Osserviamo un esempio che lo dimostra. Vogliamo ottenere la data corrente e formattarla in accordo con la convezione usata in uno specifico paese del mondo, diciamo la Francia. (Altre regioni, come la parte -di lingua francese della Svizzera utilizzano le stesse convenzioni.) +di lingua francese della Svizzera, utilizzano le stesse convenzioni.) -Le librerie delle classi Java definiscoono potenti classi di utilità, come +Le librerie delle classi Java definiscono potenti classi di utilità come `Date` e `DateFormat`. Poiché Scala interagisce direttamente con Java, non esistono le classi equivalenti nella libreria delle classi di Scala--possiamo semplicemente importare le classi dei corrispondenti package Java: @@ -114,16 +114,16 @@ semplicemente importare le classi dei corrispondenti package Java: } } -L’istruzione import di Scala è molto simile all’equivalente in Java, +L’istruzione import di Scala è molto simile all’equivalente in Java tuttavia, risulta essere più potente. Più classi possono essere importate -dallo stesso package includendole in parentesi graffe come nella prima linea +dallo stesso package includendole in parentesi graffe come nella prima riga di codice precedentemente riportato. Un’altra differenza è evidente nell’uso del carattere underscore (`_`) al posto dell’asterisco (`*`) per importare tutti i nomi di un package o di una classe. Questo perché l’asterisco è un identificatore valido (e.g. nome di un metodo), come vedremo più avanti. -Inoltre, l’istruzione import sulla terza linea importa tutti i membri +Inoltre, l’istruzione import sulla terza riga importa tutti i membri della classe `DateFormat`. Questo rende disponibili il metodo statico `getDateInstance` ed il campo statico `LONG`. @@ -145,7 +145,7 @@ Apparentemente sembra un piccolo dettaglio sintattico ma, presenta delle importanti conseguenze. Una di queste sarà esplorata nella prossima sezione. -A questo punto, riguardo l’integrazione con Java abbiamo notato che è +A questo punto, riguardo l’integrazione con Java, abbiamo notato che è altresì possibile ereditare dalle classi Java ed implementare le interfacce direttamente in Scala. @@ -155,7 +155,7 @@ direttamente in Scala. Scala è un linguaggio orientato agli oggetti (_object-oriented_) puro nel senso che *ogni cosa* è un oggetto, inclusi i numeri e le funzioni. In questo differisce da Java che invece distingue tra tipi primitivi (come `boolean` - e `int` ) e tipi referenziati. I\noltre, Java non permette la manipolazione + e `int` ) e tipi referenziati. Inoltre, Java non permette la manipolazione di funzioni come fossero valori. ### I numeri sono oggetti @@ -200,9 +200,9 @@ cardini di un interessante paradigma di programmazione chiamato Come esempio semplice del perché può risultare utile usare le funzioni come valori consideriamo una funzione timer che deve eseguire delle azione ogni secondo. Come specifichiamo l’azione da eseguire? -Logicamente, come una funzione. Questo tipo di passaggio di funzione è +Logicamente come una funzione. Questo tipo di passaggio di funzione è familiare a molti programmatori: viene spesso usato nel codice delle -interfacce utente, per registrare le funzioni di call-back richiamate +interfacce utente per registrare le funzioni di call-back richiamate quando un evento si verifica. Nel successivo programma la funzione timer è chiamata `oncePerSecond` e @@ -226,7 +226,7 @@ frase “time flies like an arrow” ogni secondo. } } -Notare che per stampare la stringa, usiamo il metodo `println` predefinito +Notare che per stampare la stringa usiamo il metodo `println` predefinito invece di quelli inclusi in `System.out`. #### Funzioni anonime @@ -253,7 +253,7 @@ invece di *timeFlies* e appare come di seguito: La presenza delle funzioni anonime in questo esempio è rivelata dal simbolo `=>` che separa la lista degli argomenti della funzione dal -suo corpo. In questo esempio la lista degli argomenti è vuota e di fatti, +suo corpo. In questo esempio la lista degli argomenti è vuota e di fatti la coppia di parentesi sulla sinistra della freccia è vuota. Il corpo della funzione `timeFlies` è lo stesso del precedente. @@ -262,7 +262,7 @@ funzione `timeFlies` è lo stesso del precedente. Come visto precedentemente Scala è un linguaggio orientato agli oggetti e come tale presenta il concetto di classe. (Per ragioni di completezza va notato che alcuni linguaggi orientati agli oggetti non hanno il concetto -di classe ma, Scala non è uno di questi.) Le classi in Scala sono dichiarate +di classe; Scala non è uno di questi.) Le classi in Scala sono dichiarate usando una sintassi molto simile a quella usata in Java. Un'importante differenza è che le classi in Scala possono avere dei parametri. Questo concetto è mostrato nella seguente definizione dei numeri complessi. @@ -285,20 +285,19 @@ del segno uguale dei metodi e deducendo che per entrambi si tratta di valori di tipo `Double`. Il compilatore non è sempre capace di dedurre i tipi come nel caso precedente; -purtroppo non c’è una regola semplice capace di dirci quando sarà in grado e -quando no. Nella pratica questo non è un problema poiché il compilatore sa quando -non è in grado di stabilire il tipo che non è stato definito +purtroppo non c’è una regola semplice capace di dirci quando sarà in grado di +farlo e quando no. Nella pratica questo non è un problema poiché il compilatore +sa quando non è in grado di stabilire il tipo che non è stato definito esplicitamente. Come semplice regola i programmatori Scala alle prime armi dovrebbero provare ad omettere la dichiarazione di tipi che sembrano semplici -da dedurre per osservare il comportamento del compilatore. Dopo qualche tempo il -programmatore avrà la sensazione di quando è possibile omettere il tipo -e quando no. +da dedurre per osservare il comportamento del compilatore. Dopo qualche tempo si +avrà la sensazione di quando è possibile omettere il tipo e quando no. ### Metodi senza argomenti Un piccolo problema dei metodi `re` e `im` è che, per essere invocati, è necessario far seguire il nome del metodo da una coppia di parentesi tonde -vuote, come mostrato di seguito: +vuote, come mostrato nel codice seguente: object ComplexNumbers { def main(args: Array[String]) { @@ -329,8 +328,8 @@ la classe `scala.AnyRef` è implicitamente usata. In Scala è possibile eseguire la sovrascrittura (_override_) dei metodi ereditati dalla super-classe. È pertanto necessario specificare esplicitamente -il metodo che si sta sovrascrivendo usando il modificatore `override`, al fine -di evitare sovrascritture accidentali. Come esempio estendiamo la nostra classe +il metodo che si sta sovrascrivendo usando il modificatore `override` per +evitare sovrascritture accidentali. Come esempio estendiamo la nostra classe `Complex` ridefinendo il metodo `toString` ereditato da `Object`. class Complex(real: Double, imaginary: Double) { @@ -341,7 +340,7 @@ di evitare sovrascritture accidentali. Come esempio estendiamo la nostra classe } -## Case Classes e Pattern Matching +## Classi Case e Pattern Matching Un tipo di struttura dati che spesso si trova nei programmi è l’albero. Ad esempio, gli interpreti ed i compilatori abitualmente rappresentano i @@ -356,23 +355,24 @@ costanti intere e variabili intere. Due esempi di tali espressioni sono `1+2` e `(x+x)+(7+y)`. A questo punto è necessario definire il tipo di rappresentazione per -dette espressioni e, a tale proposito, l’albero è la più naturale, dove -i nodi sono le operazioni (nel nostro caso, l’addizione) e le foglie -sono i valori (costanti o variabili). +dette espressioni e, a tale proposito, l’albero è la più naturale, con +i nodi che rappresentano le operazioni (nel nostro caso, l’addizione) mentre +le foglie sono i valori (costanti o variabili). -In questo albero è abitualmente rappresentato usando una super-classe +In Scala questo albero è abitualmente rappresentato usando una super-classe astratta per gli alberi e una concreta sotto-classe per i nodi o le foglie. In un linguaggio funzionale useremmo un tipo dati algebrico per -lo stesso scopo. Scala fornisce il concetto di _case classes_ che è qualcosa -che si trova nel mezzo delle due rappresentazioni. Mostriamo come può essere -usato per definire il tipo di alberi per il nostro esempio: +lo stesso scopo. Scala fornisce il concetto di _classi case_ (_case classes_) +che è qualcosa che si trova nel mezzo delle due rappresentazioni. +Mostriamo come può essere usato per definire il tipo di alberi per il nostro +esempio: abstract class Tree case class Sum(l: Tree, r: Tree) extends Tree case class Var(n: String) extends Tree case class Const(v: Int) extends Tree -Il fatto che le classi `Sum`, `Var` e `Const` sono dichiarate come case classes +Il fatto che le classi `Sum`, `Var` e `Const` sono dichiarate come classi case significa che rispetto alle classi standard differiscono in diversi aspetti: - la parola chiave `new` non è necessaria per creare un’istanza di queste @@ -398,7 +398,7 @@ qualche *ambiente* (_environment_) di valutazione. Lo scopo dell’environment `x+1` valutata nell’environment con associato il valore `5` alla variabile `x`, scritto `{ x -> 5 }`, restituisce `6` come risultato. -Inoltre dobbiamo trovare un modo per rappresentare gli environment. +Inoltre, dobbiamo trovare un modo per rappresentare gli environment. Potremmo naturalmente usare alcune strutture dati associative come una hash table ma, possiamo anche usare direttamente delle funzioni! Un environment in realtà non è altro che una funzione con associato un @@ -413,7 +413,7 @@ altri casi. Prima di scrivere la funzione di valutazione diamo un nome al tipo di environment. Potremmo usare sempre il tipo `String => Int` per gli environment -ma, semplifichiamo il programma se introduciamo un nome per questo tipo +ma semplifichiamo il programma se introduciamo un nome per questo tipo rendendo anche i cambiamenti futuri più facili. Questo è fatto in con la seguente notazione: @@ -464,18 +464,18 @@ e nominare varie parti del valore per valutare il codice che ne fa uso. Un programmatore object-oriented esperto potrebbe sorprendersi del fatto che non abbiamo definito `eval` come *metodo* della classe e delle sue -sottoclassi. Potremmo averlo fatto, perchè Scala permette la definizione di +sottoclassi. Potremmo averlo fatto perchè Scala permette la definizione di metodi nelle case classes così come nelle classi normali. Decidere quando usare il pattern matching o i metodi è quindi una questione di gusti ma, ha anche implicazioni importanti riguardo l’estensibilità: - quando si usano i metodi è facile aggiungere un nuovo tipo di nodo definendo una sotto classe di `Tree` per esso; d’altro canto, aggiungere - una nuova operazione per manipolare l’albero è noioso, richiede la + una nuova operazione per manipolare l’albero è noioso e richiede la modifica di tutte le sotto classi `Tree`; - quando si usa il pattern matching la situazione è ribaltata: aggiungere un nuovo tipo di nodo richiede la modifica di tutte le - funzioni in cui si fa pattern matching sull’albero, per prendere in + funzioni in cui si fa pattern matching sull’albero per prendere in esame il nuovo nodo; d’altro canto, aggiungere una nuova operazione è semplice, basta definirla come una funzione indipendente. @@ -489,8 +489,8 @@ operazione: derivazione, zero altrimenti, 3. la derivata di una costante è zero. -Queste regole possono essere tradotte quasi letteralmente in codice Scala, -per ottenere la seguente definizione: +Queste regole possono essere tradotte quasi letteralmente in codice Scala e +ottenere la seguente definizione: def derive(t: Tree, v: String): Tree = t match { case Sum(l, r) => Sum(derive(l, v), derive(r, v)) @@ -504,17 +504,17 @@ matching. Prima di tutto l’istruzione `case` per le variabili ha un controllo fa si che il pattern matching è eseguito solo se l’espressione è vera. Qui viene usato per esser sicuri che restituiamo la costante `1` solo se il nome della variabile da derivare è lo stesso della variabile -di derivazione `v`. La seconda nuova feature del pattern matching qui +di derivazione `v`. La seconda nuova caratteristica del pattern matching qui introdotta è la *wild-card*, scritta `_`, che corrisponde a qualunque valore, senza assegnargli un nome. -Non abbiamo esplorato del tutto la potenza del pattern matching ma, ci +Non abbiamo esplorato del tutto la potenza del pattern matching ma ci fermiamo qui per brevità. Vogliamo ancora osservare come le due -precedenti funzioni lavorano in un esempio reale. A tale scopo, +precedenti funzioni lavorano in un esempio reale. A tale scopo scriviamo una semplice funzione `main` che esegue diverse operazioni sull’espressione `(x+x)+(7+y)`: prima calcola il suo valore nell’environment `{ x -> 5, y -> 7 }`, dopo calcola la -derivata relativa a `x` e poi ad `y`. +derivata relativa ad `x` e poi ad `y`. def main(args: Array[String]) { val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) @@ -525,7 +525,7 @@ derivata relativa a `x` e poi ad `y`. println("Derivative relative to y:\n " + derive(exp, "y")) } -Eseguendo questo programma, otteniamo l’output atteso: +Eseguendo questo programma otteniamo l’output atteso: Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) Evaluation with x=5, y=7: 24 @@ -534,7 +534,7 @@ Eseguendo questo programma, otteniamo l’output atteso: Derivative relative to y: Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) -Esaminando l’output, notiamo che il risultato della derivata dovrebbe +Esaminando l’output notiamo che il risultato della derivata dovrebbe essere semplificato prima di essere visualizzato all’utente. La definizione di una funzione di semplificazione usando il pattern matching rappresenta un interessante (ma sorprendentemente @@ -586,7 +586,7 @@ tipo `Object` in Java dato che è altresì il super-tipo dei tipi base come Per rendere confrontabili gli oggetti di una classe è quindi sufficiente definire i predicati con cui testare uguaglianza ed minoranza e unire la -precedente classe `Ord`. Come esempio, definiamo una classe `Date` che +precedente classe `Ord`. Come esempio definiamo una classe `Date` che rappresenta le date nel calendario Gregoriano. Tali date sono composte dal giorno, dal mese e dall’anno che rappresenteremo tutti con interi. Iniziamo definendo la classe `Date` come segue: @@ -613,7 +613,7 @@ Arriviamo alla seguente definizione: o.day == day && o.month == month && o.year == year } -Questo metofo fa uso di due metodi predefiniti `isInstanceOf` e `asInstanceOf`. +Questo metodo fa uso di due metodi predefiniti `isInstanceOf` e `asInstanceOf`. Il primo, `isInstanceOf`, corrisponde all’operatore `instanceOf` di Java e restituisce true se e solo se l’oggetto su cui è applicato è una istanza del tipo dati. Il secondo, `asInstanceOf`, corrisponde all’operatore di cast in @@ -652,7 +652,7 @@ informati dei problemi relativi alla mancanza della programmazione generica nel loro linguaggio, un’imperfezione risolta in Java 1.5. La programmazione generica riguarda la capacità di scrivere codice -parametrizzato dai tipi. Per esempio, un programmatore che scrive una +parametrizzato dai tipi. Per esempio un programmatore che scrive una libreria per le liste concatenate può incontrare il problema di decidere quale tipo dare agli elementi della lista. Dato che questa lista è stata concepita per essere usata in contesti differenti, non è possibile @@ -662,7 +662,7 @@ restrittivo. I programmatori Java hanno fatto ricorso all’uso di `Object`, che è il super-tipo di tutti gli oggetti. Questa soluzione è in ogni caso ben lontana -dall’esser ideale, poiché non funziona per i tipi base (`int`, `long`, `float`, +dall’esser ideale perché non funziona per i tipi base (`int`, `long`, `float`, ecc.) ed implica che molto type casts dinamico deve esser fatto dal programmatore. @@ -690,8 +690,8 @@ tipi numerici, `false` per il tipo `Boolean`, `())`per il tipo `Unit` e `null` per tutti i tipi oggetto. Per usare la classe `Reference` è necessario specificare quale tipo usare per -il tipo parametro `T`, il tipo di elemento contenuto dalla cella. Adr esempio, -per creare ed usare una cella che contiene un intero, si potrebbe scrivere il +il tipo parametro `T`, il tipo di elemento contenuto dalla cella. Ad esempio, +per creare ed usare una cella che contiene un intero si potrebbe scrivere il seguente codice: object IntegerReference { @@ -712,5 +712,5 @@ poiché è stata dichiarata per memorizzare un intero. Questo documento ha fornito una veloce introduzione del linguaggio Scala e presentato alcuni esempi di base. Il lettore interessato può continuare, per esempio, leggendo il documento *Scala By Example* che contiene esempi molti più -avanzati e, consultare al bisogno la documentazione +avanzati e consultare al bisogno la documentazione *Scala Language Specification*.