Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide...

36
Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Programmazione I Caratteristiche fondamentali del linguaggio di programmazione Scala Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379

Transcript of Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide...

Page 1: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Programmazione I

Caratteristiche fondamentali del linguaggio di programmazione Scala

Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379

Page 2: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

[Dedica]

Page 3: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

Indice

Indice .................................................................................................................................................. III Introduzione ......................................................................................................................................... 4 Capitolo 1: Programmazione Funzionale ............................................................................................. 5

1.1 Caratteristiche principali ............................................................................................................ 5

1.2 Benefici della programmazione funzionale ............................................................................... 7 1.3 Evoluzione dei linguaggi di tipo funzionale .............................................................................. 7

Capitolo 2: Scala .................................................................................................................................. 9 2.1 Modalità di esecuzione ............................................................................................................. 10

2.2 Sintassi ..................................................................................................................................... 11 2.2.1 Variabili e costanti ....................................................................................................... 11

2.2.2 Definizione delle classi ................................................................................................ 13 2.2.3 Tratti ............................................................................................................................. 16

2.3 Costrutti principali ................................................................................................................... 18 2.3.1 Costrutto if ................................................................................................................... 18 2.3.2 Costrutto for ................................................................................................................. 18

Capitolo 3: Scala - Aspetti Avanzati .................................................................................................. 20 3.1 Gerarchia dei tipi ...................................................................................................................... 20

3.2 Ambiti di visibilità ................................................................................................................... 22 3.3 Uguaglianza tra oggetti ............................................................................................................ 23 3.4 Strutture Dati ............................................................................................................................ 25

3.4.1 Tuple ............................................................................................................................ 25 3.4.2 Liste .............................................................................................................................. 26

3.4.3 Vettori .......................................................................................................................... 27 3.4.4 Collezioni parallele ...................................................................................................... 28

3.4.5 Mappe........................................................................................................................... 28 3.5 Cenni sulla programmazione concorrente................................................................................ 29

Appendice: Esempi di codice ............................................................................................................. 31 Client – Server ............................................................................................................................... 31 Conteggio parole ............................................................................................................................ 33

Conclusioni ........................................................................................................................................ 34 Bibliografia ........................................................................................................................................ 36

Page 4: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

4

Introduzione

Il seguente elaborato descriverà quali sono le caratteristiche fondamentali del linguaggio

di programmazione Scala.

Sono molte le società che negli ultimi anni stanno adoperando Scala come linguaggio

principale nello sviluppo di nuovo software e altre ancora stanno migrando verso tale

linguaggio riscrivendo gran parte di software già esistente. Grandi compagnie come

Google oppure Apple hanno scelto di formare team di sviluppatori Scala per progetti futuri

oppure Twitter che ha deciso di abbandonare Ruby convertendo tutta la piattaforma in

Scala. L’enorme successo che sta vivendo Scala è dovuto alle sue caratteristiche che

verranno descritte nel corso di tale elaborato e soprattutto alla sua capacità di fornire le

migliori prestazioni nel caso di sistemi concorrenti e distribuiti.

Nel primo capitolo si effettuerà una panoramica sulla programmazione funzionale

analizzando le sue caratteristiche principali, i benefici e la sua evoluzione nel tempo.

Nel secondo capitolo saranno descritti i costrutti principali del linguaggio di

programmazione Scala e i suoi aspetti sintattici.

Infine, nel terzo capitolo, si analizzeranno gli aspetti avanzati di Scala riguardante la

componente Object Oriented e funzionale.

Page 5: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

5

Capitolo 1: Programmazione Funzionale

Come ben noto lo sviluppo dei sistemi software nel tempo è diventato sempre più

complesso, per cui risulta sempre più importante strutturarli nel modo migliore. Software

ben strutturati sono facili da scrivere e permettono di avere una collezione di moduli che

possono essere utilizzati nuovamente per ridurre il costo di sviluppo. Nel seguente capitolo

verrà spiegato perché la programmazione funzionale, attraverso le sue caratteristiche,

contribuisce allo sviluppo modulare.

1.1 Caratteristiche principali

La programmazione funzionale è uno stile fondamentale di programmazione in cui

l’esecuzione di un programma avviene attraverso lo svolgimento di espressioni

matematiche e l’utilizzo di variabili immutabili.

Caratteristica necessaria della programmazione funzionale è quella di avere funzioni di

tipo “first-class”, precisamente “higher-ordered”, ovvero funzioni che possono essere

utilizzate come argomento o come valore di ritorno di un’altra funzione. Un esempio di

funzione higher-ordered è la funzione MAP la quale ha come argomenti una funzione e

una lista e ritorna come valore la lista trasformata applicando la funzione ad ogni elemento

della lista.

La funzione double è applicata alla lista di interi (1,2,3,4,5) e restituisce la lista con i

(map double (list 1 2 3 4 5)) => (2 4 6 8 10)

Page 6: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

6

valori raddoppiati.

Programmi di tipo funzionale non hanno “effetti collaterali”, questo significa che

l’esecuzione di una funzione produce solo il risultato delle sue operazioni senza

modificare lo stato di altre funzioni. Questa caratteristica è molto importante perché

elimina il verificarsi di molti bug e, inoltre, rende irrilevante l’ordine di esecuzione delle

varie funzioni all’interno del programma in quanto ogni funzione è indipendente dall’altra.

Quindi, a valle di queste considerazioni, risulta molto più semplice verificare il corretto

funzionamento del software attraverso le operazioni di testing.

Dal momento in cui le espressioni possono essere valutate in qualsiasi momento, si

possono liberamente rimpiazzare le variabili con il loro valore e viceversa - ovvero, i

programmi godono della proprietà di “trasparenza referenziale”. Si ricorda che per

trasparenza referenziale si intende che una funzione fornisce lo stesso risultato, dati gli

stessi input, in tutte le istanze. Questa libertà aiuta a rendere programmi funzionali

matematicamente più trattabili rispetto ai loro omologhi convenzionali.

Un’altra importante proprietà è la “lazy evaluation” (valutazione pigra) che consiste

nell’eseguire una determinata operazione solo quando è realmente necessaria.

Ad esempio se dobbiamo applicare la funzione MAP su una lista di elementi, questa verrà

eseguita solo quando tutti gli elementi della lista sono necessari per altre operazioni.

Questa proprietà permette di:

definire strutture dati potenzialmente infinite;

aumentare le performance evitando di eseguire operazioni non richieste;

definire strutture di controllo come astrazioni invece che come primitive;

Ampliamente utilizzata è la “Tail Call Optimization” che permette di non occupare

ulteriore spazio nello stack quando c’è una chiamata a funzione. Questo è possibile

quando l’ultima operazione di una funzione è quella di chiamare sé stessa in modo

ricorsivo. Nella programmazione funzionale, infatti, le iterazioni avvengono attraverso

l’uso della ricorsione. Funzioni ricorsive non fanno altro che richiamare sé stesse finché

non è raggiunto il caso base. Quindi grazie alla tecnica della “Tail Call Optimization” si

rende più efficiente la ricorsione e soprattutto evita che si crei uno scenario di stack

Page 7: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

7

overflow.

È importante notare come il problema della concorrenza viene risolto senza l’utilizzo di

particolare tecniche come ad esempio semafori o monitor. Infatti basandoci sulla

condizione di immutabilità delle variabili non si presenta più il problema della race

condition.

1.2 Benefici della programmazione funzionale

Le caratteristiche introdotte nel paragrafo precedente permettono di utilizzare la

programmazione funzionale per sviluppare programmi più strutturati rispetto alla

controparte imperativa. Per creare un programma strutturato è necessario sviluppare delle

astrazioni e suddividere il programma in più componenti che si interfacciano tra di loro

attraverso tali astrazioni.

I linguaggi funzionali raggiungono perfettamente l’obiettivo rendendo semplice la

creazione di tali astrazioni. È facile, ad esempio, astrarre righe di codice ricorrenti creando

una funzione di tipo high-order che permette di ottenere un codice più dichiarativo e

comprensibile.

1.3 Evoluzione dei linguaggi di tipo funzionale

La nascita della programmazione funzionale è strettamente legata al “Lambda Calcolo”,

un ramo della logica nato tra gli anni ’20 e ’30. Il Lambda Calcolo è stato sviluppato da

logici (la prima versione fu definita da Church nel 1932) con l’obiettivo di scoprire come

definire le funzioni in modo formale e come usare questo formalismo come base per la

matematica. Esso rappresenta un semplice linguaggio formale di funzioni così potente da

poter definire quasi tutta la matematica conosciuta. Ovviamente tale linguaggio non è stato

definito per scopi informatici anche perché non esisteva nessun tipo di computer o

calcolatore.

Successivamente, nel 1960, nasce il primo linguaggio funzionale con l’introduzione della

prima versione di LISP da parte di McCarthy. Negli anni sono nati molti dialetti di tale

Page 8: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

8

linguaggio LISP come ad esempio Scheme e il Common LISP. LISP, ad oggi, è

ampliamente utilizzato come linguaggio per l’Intelligenza Artificiale, ad esempio nel

controllo real-time di robots e come macro linguaggio per software come AutoLISP per

Autocad.

Alla fine degli anni ’70 Backus esprime la sua idea sulla realizzazione di un linguaggio di

programmazione puro e high-order con una sintassi molto vicina alla logica combinatoria.

Egli definisce così il linguaggio FP (Function Programming) che ha molto influenzato lo

sviluppo dei linguaggi funzionali. Elemento principale di FP sono le funzioni di tipo high-

order.

Nello stesso periodo, nell’università di Edimburgo, è stato definito il linguaggio ML

(Meta-Language) per esprimere strategie di ricerca tramite il theorem prover LCF. Eredita

da FP le funzioni di tipo high-order introducendo importanti novità come la deduzione

automatica del tipo ed è utilizzato soprattutto in compilatori, analizzatori e theorem

provers. Essendo general purpose, però, trova applicazione anche in bioinformatica oppure

in sistemi finanziari. Come LISP anche ML possiede molti dialetti, i più diffusi sono SML

(Standard ML) e CAML.

Nel 1985 David Turner sviluppa Miranda, un linguaggio di programmazione molto simile

a ML. È uno dei primi linguaggi a possedere la proprietà della lazy evaluation.

Nel 1986 Ericsson introduce Erlang, un linguaggio di programmazione funzionale che

lavora con processi concorrenti. Inizialmente è stato utilizzato per applicazioni telematiche

ma è diventato un linguaggio general purpose. Infatti Erlang rappresenta il linguaggio

funzionale più utilizzato nel mondo dell’industria.

Infine nel 1990 nasce Haskell con l’obiettivo di diventare lo standard dei linguaggi di

programmazione funzionale. È adottato in vari scenari come quello Aerospaziale, difesa e

finanza.

Page 9: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

9

Capitolo 2: Linguaggio di programmazione Scala

Lo sviluppo di Scala, che sta per “Scalable Language”, inizia nel 2001 da parte di Martin

Odersky insieme ai ricercatori dell’École Polytechnique Fédérale de Lausanne (EPFL).

Dopo il rilascio di una prima versione interna nel 2003, la prima versione pubblica di

Scala fu annunciata nel 2004, prima su piattaforma Java e successivamente su quella

.NET. Nel 2006 Scala raggiunge la seconda versione mentre nel 2012 viene ufficialmente

abbandonato il supporto alla piattaforma .NET. Il 17 Gennaio 2012 il team sviluppatore di

Scala ha ricevuto un finanziamento di 2.3 milioni di euro dal Consiglio Europeo della

Ricerca.

Scala è un linguaggio di programmazione multi-paradigma che unisce la programmazione

ad oggetti con quella funzionale.

È un linguaggio ad oggetti perché ogni elemento è un oggetto. Il tipo e il comportamento

degli oggetti è descritto attraverso le classi. Il design pattern creazionale Singleton è

supportato per la creazione di oggetti mentre il design pattern comportamentale Visitor

viene utilizzato attraverso il pattern matching. Attraverso l’utilizzo di classi implicite è

possibile estendere classi esistenti con nuove funzioni. Scala, inoltre, è stato progettato per

lavorare con linguaggi meno puri ma orientati agli oggetti come Java.

Infine esso è un linguaggio funzionale perché ogni funzione è vista come un valore e

quindi l’annidamento, funzioni di tipo high-order e strutture dati immutabili sono

perfettamente supportate. A differenza di altri linguaggi funzionali, Scala permette di

ottenere uno stile di programmazione più funzionale in modo facile e graduale. Infatti, con

una crescente esperienza e familiarità con il codice, si può facilmente sostituire la

mutabilità dei software che si sviluppano con pattern funzionali molto più sicuri.

Page 10: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

10

Scala permette di avere le migliori perfomance con software per server scalabili che fanno

uso di processi concorrenti e sincronizzati, per il parallelismo di architetture multi core e

di processi distribuiti nel cloud.

2.1 Modalità di esecuzione

Una volta installato, Scala permette di eseguire un programma in due modalità differenti:

interattiva e script.

In modalità interattiva è possibile eseguire istruzioni tramite una shell interattiva fornita

direttamente dall’ambiente Scala. Aprendo un terminale (in questo esempio si sta

utilizzando un ambiente Windows) e scrivendo la seguente istruzione si può osservare

quale versione di Scala è installata:

Infine si utilizza l’istruzione println per stampare una stringa:

Invece, in modalità script, sfruttando un qualsiasi editor di testo si crea un file con

estensione “.scala” come mostrato di seguito:

object HelloWorld {

def main(args: Array[String]) {

println("Hello, world!")

}

}

Page 11: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

11

Si può facilmente compilare il programma Scala appena salvato attraverso il comando

scalac che salverà nuovi file con estensione ”.class” nella cartella del programma tra cui

uno chiamato ”nomeprogramma.class” che rappresenta il bytecode che verrà eseguito

dalla Java Virtual Machine (JVM) attraverso il comando scala.

2.2 Sintassi

Nel seguente paragrafo saranno descritti gli aspetti sintattici più importanti di Scala come

la definizione di variabili, classi e tratti.

È interessante notare che in Scala l’uso del punto e virgola come terminatore di linea di

codice è a discrezione del programmatore perché il compilatore è in grado di determinare

automaticamente la fine della riga come la fine dell’istruzione.

2.2.1 Variabili e costanti

Scala permette di definire le variabili attraverso l’uso di due parole chiave:

var instanzia una variabile che può cambiare valore ed è definita come

variabile mutabile;

val instanzia una variabile che non può cambiare valore ed è definita come

variabile immutabile.

Nel primo caso la sintassi è la seguente:

mentre nel secondo caso:

In generale per poter dichiarare una variabile in Scala si usa la seguente notazione:

var Nome : String = "Davide"

val Età : Int = 23

var o var Nome_Variabile : Tipo = [Valore Iniziale]

Page 12: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

12

Se invece non si vuole associare un valore iniziale alla varabile appena dichiarata si può

utilizzare la seguente sintassi:

Quando si assegna un valore iniziale ad una variabile il compilatore di Scala riesce ad

individuare il tipo di variabile dichiarata in base al valore assegnato, questo perché Scala

supporta la type inference (inferenza di tipo). In questo modo l’esempio precedente può

essere scritto come segue:

Automaticamente il compilatore riconosce il tipo della variabile “Nome” come String e

della variabile “Età” come Int. Se ad esempio si vuole dichiarare una variabile di tipo

double utilizzando la type inference basta scrivere nel seguente modo:

Da notare che un’istruzione come quella riportata in seguito, inserita dopo la precedente

riga di codice, comporta un errore in fase di compilazione perché la variabile “Età”

essendo dichiarata come val è immutabile:

Se invece si vuole dichiarare una variabile di tipo float si utilizza la seguente notazione:

var Nome : String; val Età : Int;

var Nome = "Davide"; val Età = 23;

val Età = 23.0;

Età = 35.0;

var Peso = 70.0f;

Page 13: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

13

Si presenta un piccolo esempio di codice che spiega in che modo avviene la dichiarazione

di variabili in Scala:

2.2.2 Definizione delle classi

Si presenta un semplice esempio per capire in che modo avviene la definizione delle classi

in Scala. La seguente classe definisce due variabili x e y e un metodo move, il quale non

ritorna alcun valore. Il nome della classe svolge la funzione di costruttore a cui vengono

passati un certo numero di parametri: nel nostro esempio tali parametri sono xc e yc.

La definizione delle funzioni in Scala avviene attraverso la seguente sintassi:

class Point(xc: Int, yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy println ("Posizione del punto x : " + x); println ("Posizione del punto y : " + y); } }

def nomeFunzione ([lista di parametri]) : [return tipo] = { corpo della funzione return [espressione] }

object Esempio { def main(args: Array[String]) { var myVar :Int = 10; val myVal :String = "Hello Scala con dichiarazione di tipo."; var myVar1 = 20; val myVal1 = "Hello Scala senza dichiarazione di tipo."; println(myVar); println(myVal); println(myVar1); println(myVal1); } }

Page 14: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

14

Sia il tipo di ritorno che la lista di parametri sono opzionali in Scala. Una funzione che non

ha nessun tipo di ritorno prende il nome di procedura e utilizza il tipo Unit come ritorno,

tipo equivalente al void in Java.

Una classe esistente può essere estesa utilizzando la parola chiave extends ma con due

restrizioni: in primo luogo il metodo dell’override richiede l’uso della parola chiave

override e come seconda restrizione solo il costruttore primario può passare parametri al

costruttore della classe originale.

Si presenta un esempio di estensione di una classe utilizzando la classe Point

precedentemente dichiarata creando una nuova classe estesa Location. Attraverso la parola

chiave extends è possibile ereditare da Point tutti i membri non privati e di creare il tipo

Location come sottotipo di Point. In questo modo la classe Point prende il nome di

superclasse mentre Location quello di sottoclasse. Scala permette l’ereditarietà da una

sola superclasse.

Page 15: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

15

class Point(xc: Int, yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy println ("Posizione del punto x : " + x); println ("Posizione del punto y : " + y); } } class Location(override val xc: Int, override val yc: Int, val zc :Int) extends Point(xc, yc){ var z: Int = zc def move(dx: Int, dy: Int, dz: Int) { x = x + dx y = y + dy z = z + dz println ("Posizione del punto x : " + x); println ("Posizione del punto y : " + y); println ("Posizione del punto z : " + z); } } object Esempio { def main(args: Array[String]) { val loc = new Location(10, 20, 15); loc.move(10, 10, 5); } }

Page 16: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

16

2.2.3 Tratti

Uno strumento molto potente che offre Scala agli sviluppatori sono i Tratti i quali

permettono di creare nuove classi costituite da metodi appartenenti ad altre classi. Essi

rappresentano l’alternativa di Scala all’ereditarietà multipla offrendo una soluzione

migliore rispetto alle classi astratte e le interfacce utilizzate in Java in quanto queste ultime

presentano delle limitazioni. Infatti le interfacce non contengono l’implementazione dei

metodi che definiscono mentre una nuova classe non può estendere più classi astratte. Si

presenta un semplice esempio di utilizzo dei Tratti in cui si definisce una classe astratta

“Volatile” e i tratti “Nuoto” e “Volo” per definire vari tipi di uccelli che possono sia

nuotare che volare o solo una delle due.

Tipico problema dell’ereditarietà multipla è il Diamond Problem che Scala risolve

attraverso la linearizzazione. Il Diamond Problem è un’ambiguità che si verifica quando

due classi B e C ereditano da una classe A e una classe D eredita sia da B e C. Se D

invoca un metodo definito in A da quale classe viene ereditato?

abstract class Volatile trait Nuoto { def nuotare() = println(“Sto Nuotando”) } trait Volo { def messaggio : String def volare() = println(“messaggio”) } class Pinguino extends Volatile with Nuoto class Fregata extends Volatile with Volo { val messaggio = “Posso solo volare” } class Piccione extends Volatile with Nuoto with Volo { val messaggio = "Sono un buon volatile" } class Falco extends Volatile with Nuoto with Volo { val messaggio = "Sono un eccellente volatile" }

Page 17: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

17

Si ricrea la seguente situazione in Scala utilizzando i Tratti.

Se viene istanziata una nuova classe D che estende sia B che C il metodo ritornerà true o

false?

Il risultato dipende dall’ordine in cui estendiamo la classe con i tratti. Infatti se la classe D

è istanziata nel seguente modo il metodo ritornerà false.

Invece se istanziata in modo diverso ritornerà true.

abstract class A{ def stato() : Boolean } trait B extends A{ override def stato() : Boolean = true } trait C extends A{ override def stato() : Boolean = false }

val D = new A with B with C{ println(d.stato) }

val D = new A with C with B{ println(d.stato) }

Page 18: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

18

2.3 Costrutti principali

Nel seguente paragrafo saranno illustrati in che modo funzionano in Scala i tipici costrutti

della programmazione come le istruzioni condizionali e diversi tipi di cicli.

2.3.1 Costrutto if

Nei linguaggi di programmazione un’istruzione condizionale può solo controllare

l’esecuzione del codice senza produrre alcun valore, in Scala, invece, l’istruzione if è

un’espressione e in quanto tale può ritornare un singolo valore. Ciò significa che una

generica istruzione if del tipo:

può essere riscritta in questo modo:

2.3.2 Costrutto for

Scala offre strumenti avanzati per la gestione dei cicli for come il filtering e lo yielding.

Prima di analizzare in dettaglio tali strumenti si presenta un generico esempio

dell’istruzione for in Scala in cui sono istanziati una classe Persona e una lista di oggetti

di tipo Persona:

if(a == b) result = “E’ vero!” else result = “Non è vero!”

result = if(a == b) “E’ vero!” else “Non è vero!”

class Persona(val nome: String, val femmina: Boolean) val popolazione = List( new Persona("Davide Casillo", false), new Persona("Giuseppe Parente", false), new Persona("Gabriella Esposito", true), new Persona("Simona Castaldi", true), new Persona("Mario Casillo", false) new Persona("Francesca Casillo", true))

Page 19: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

19

Per poter stampare a video tutti gli elementi della lista basterà scrivere la seguente

istruzione:

Uno strumento molto utile che offre Scala è il filtering che permette di filtrare gli oggetti

di una lista per estrarre solo gli elementi che soddisfano il parametro di ricerca.

Riprendendo l’esempio precedente si applica tale strumento alla lista di Persone per

ricavare tutti gli uomini di cognome “Casillo”:

Un altro importante strumento è lo yielding che permette di creare una nuova lista,

partendo da una già esistente, applicando un nuovo algoritmo o funzione agli elementi

estratti. Sempre dall’esempio precedente si può creare una nuova lista denominata “Nomi”

che contenga solo i nomi delle persone:

for(Persona <- popolazione) println(person.nome);

for(persona: Persona <- popolazione if !persona.femmina if persona.nome.contains("Casillo")) println(persona.nome)

val nomi = for(Persona <- popolazione)yield persona.nome for(nome <- nomi) println(nomi)

Page 20: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

20

Capitolo 3: Scala - Aspetti Avanzati

Nel seguente capitolo saranno mostrati in dettaglio alcune caratteristiche di Scala

riguardante la sua componente Object Oriented e ponendo l’attenzione soprattutto sulla

parte funzionale.

3.1 Gerarchia dei tipi

In Scala ogni valore è un oggetto sia che esso sia numerico o una funzione. Inoltre essendo

Scala un linguaggio basato su classi si ha che ogni valore è un’istanza di una classe.

La figura che segue mostra in che modo è configurata la gestione delle classi in Scala:

Page 21: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

21

La superclasse da cui derivano tutte le classi di Scala è Any che definisce metodi final

come ad esempio !=, ==, asIstanceOf[T] (per la conversione di tipo). Possiede due dirette

sottoclassi che sono AnyVal e AnyRef che rappresentano rispettivamente la classe che

contiene tutti i tipi valore e la classe a cui appartengono tutti i tipi riferimento. La prima,

AnyVal, possiede tutte istanze di valore immutabile e nessun tipo può essere istanziato

con la parola chiave new. Invece AnyRef rappresenta l’equivalente della classe Object di

Java da cui derivano tutte le classi appartenenti a Java. Un’altra sottoclasse importante di

AnyRef è ScalaObject da cui derivano tutte le classi fornite dalla piattaforma Scala come

ad esempio List, Seq, Option.

Alla base della figura si trova la classe Null che rappresenta il valore nullo solo per i tipi

appartenenti a AnyRef mentre Nothing lo è per i tipi appartenenti a AnyVal. In realtà

essendo Nothing una derivazione di tutte le classi esso può essere esteso anche ai tipi

appartenenti alla classe AnyVal.

Altre classi importanti che non sono rappresentante in figura sono Nil e Option. La prima

viene utilizzato per istanziare una lista vuota:

La seconda, invece, è utile quando bisogna istanziare un valore che non è ancora definito

in modo che lo sviluppatore non utilizzi null evitando di causare un errore del tipo

NullPointerException come accade spesso in Java. La classe Option può assumere solo

due valori: None che indica un valore assente e Some, invece, che contiene un certo valore.

Con la seguente riga di codice si istanzia una variabile x di tipo String con nessun valore

assegnato:

var lista : List [Int] = Nil

var x : Option[String] = None

Page 22: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

22

3.2 Ambiti di visibilità

Rispetto ad altri linguaggi di programmazione, come ad esempio Java, Scala offre un

numero maggiore di ambiti di visibilità che permettono di gestire in modo più dettagliato

l’accesso degli identificatori all’interno del programma. Gli ambiti di visibilità proposti

sono mostrati di seguito in ordine dal “più restrittivo” al “più aperto”:

object-private: rappresenta la modalità di accesso più restrittiva in quanto il

metodo così dichiarato sarà disponibile solo per l’istanza corrente

dell’oggetto considerato. Altre istanze della stessa classe non possono

accedere al metodo. Per poter dichiarare un metodo come object-private è

necessario utilizzare la parola chiave private[this] come mostrato

nell’esempio seguente:

private: il metodo è accessibile solo dalla classe in cui è stato definito e da

tutte le altre istanze della stessa classe. Rappresenta l’equivalente private di

Java. Essendo privato il metodo non è disponibile alle sottoclassi della classe

in cui è definito. Affinché si possa dichiarare un metodo come privato

bisogna anteporre la parola chiave private:

protected: il metodo è accessibile solo dalla classe in cui è definito e dalle

sue sottoclassi. Mentre in Java un metodo protected è accessibile da altre

classi appartenenti allo stesso package, in Scala questo non è possibile

rendendo la modalità protected più restrittiva. Si utilizza la parola chiave

protected per dichiarare un metodo come protetto:

private[this] def myFunction = true

private def myFunction = true

protected def myFunction = true

Page 23: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

23

package: affinché un metodo sia disponibile per tutte le classi appartenenti

allo stesso package Scala mette a disposizione la modalità di accesso

package. Si utilizza la parola chiave private[packageName] come mostrato

nell’esempio seguente:

public: se non è utilizzata alcuna parola chiave nella definizione del metodo

questo è da ritenersi pubblico e quindi accessibile da tutte le classi di

qualsiasi package:

3.3 Uguaglianza tra oggetti

In Scala gli operatori “==” e “!=” effettuano un controllo per valore oppure per

riferimento in base al tipo di variabile che devono confrontare, al contrario di Java in cui

eseguono un controllo per riferimento indipendentemente dal tipo di variabile. Entrambi

gli operatori sono definiti come final (cioè non possono essere sovrascritti in una

sottoclasse o tratto) nella classe Any e utilizzano il metodo equals, che è sempre definito

nella classe Any ma non come final. Quando Scala deve confrontare due variabili di tipo di

valore utilizza l’uguaglianza naturale (numerica o booleana) effettuando, quindi, un

controllo per valore. Nel seguente esempio si può notare la differenza nella gestione

dell’uguaglianza di variabili di tipo di valore prima in Scala e poi in Java:

package model{ class Università{ private[model] def myCampus{} } }

def myFunction = true

//Scala var str1 = "Davide" var str2 = "Davide" println(str1 == str2) //Ritorna true

//Java String s1 = new String("Gabriella"); String s2 = new String("Gabriella"); System.out.println(s1 == s2); //Ritorna false

Page 24: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

24

Invece quando bisogna confrontare variabili di tipo di riferimento gli operatori == e != si

comportano come alias del metodo equals appartenente al java.lang.Object. Quindi, in

questi casi, Scala effettua un controllo per riferimento.

Affinché sia possibile utilizzare questi operatori nel modo che ci si aspetta con tipi definiti

dall’utente è necessario sovrascrivere il metodo equals per assicurare che confronti i valori

nel modo giusto.

Si supponga di avere una classe Persona con un solo parametro “nome” di tipo stringa e

due istanze di tale classe con lo stesso nome. Utilizzando l’operatore == oppure il metodo

equals per il confronto del nome ci si aspetta un risultato positivo, ma non è così.

Si può risolvere tale inconveniente andando a sovrascrivere il metodo equals:

Un’alternativa all’override del metodo equals (oppure toString o hashCode) è quella di

utilizzare classi definite con la parola chiave case definite case class per le quali sono già

implementati i metodi equals e hashCode senza bisogno di sovrascrittura.

class Persona(val nome: String) var persona1 = new Persona("Davide") var persona2 = new Persona("Davide") persona1 == persona2 //Dovrebbe essere true ma ritorna false

class Persona(val nome: String) { override def equals(that: Any) : Boolean = { that.isInstanceOf[Persona] && (this.hashCode() == that.asInstanceOf [Persona].hashCode()); } override def hashCode = name.hashCode } var persona1 = new Persona("Davide") var persona2 = new Persona("Davide") persona1 == persona2 //Vero

Page 25: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

25

3.4 Strutture Dati

Nel seguente paragrafo verranno descritte le principali strutture dati che Scala offre ai

propri sviluppatori. La prima struttura dati analizzata sarà la tupla che rappresenta la

struttura dati più semplice tra quelle descritte. Successivamente si passerà alle liste che

permettono di avere ottime prestazioni con operazioni di accesso agli elementi in testa. In

seguito saranno approfondite strutture dati più complesse come i vettori e le collezioni

parallele che garantiscono migliori prestazioni nella gestione di dati di grandi dimensioni.

Infine saranno descritte le mappe definite anche come Hash table.

3.4.1 Tuple

Un tupla rappresenta una collezione di due o più valori in cui ogni valore può essere di

tipo diverso rispetto agli altri elementi appartenenti alla stessa tupla. Si può istanziare una

tupla utilizzando la seguente sintassi:

La variabile t è una tupla contenente tre elementi di tipo intero, stringa e float. In

alternativa si può utilizzare una forma ridotta come nell’esempio seguente:

Si possono istanziare tuple di massimo 22 elementi quindi se si necessita di maggior

spazio bisogna utilizzare altri tipi di strutture dati come vedremo nei paragrafi successivi.

Il metodo che permette di accedere agli elementi di una tupla è il seguente:

val t = new Tuple3(28, "Davide", 7.5)

val twoInts = (3,9) val intAndString = (7, "Casillo")

val t = (1,2,3,4) println(“Stampo il primo elemento della tupla:”+ t._1) val somma = t._1 + t._2 + t._3 + t._4 println(“Stampo la somma:” + somma)

Page 26: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

26

3.4.2 Liste

La lista rappresenta una collezione di elementi ordinati molto più performante della tupla

perché non ha limiti di dimensione e supporta funzioni che permettono di eseguire

operazioni su tutti i suoi elementi. La sintassi per creare una lista è la seguente:

Si possono creare liste di qualsiasi tipo come mostrato nell’esempio seguente:

Tutte le liste possono essere definite utilizzando due elementi fondamentali: una testa

definita dall’operatore :: e una coda Nil che viene utilizzata anche per definire una lista

vuota. Tutte le liste create nell’esempio precedente possono essere così ridefinite:

Le operazioni basilari che possono essere effettuate sono:

head: restituisce il primo elemento della lista.

tail: restituisce l’ultimo elemento della lista.

isEmpty: ritorna true se la lista è vuota altrimenti false.

Attraverso l’operatore ::: oppure i metodi List.:::() o List.concat() è possibile unire due o

più liste insieme. Si presenta un esempio:

// Lista di stringhe val frutta: List[String] = List("uva", "anguria", "pesca") // Lista di interi val numeri: List[Int] = List(1, 2, 3, 4) // Lista vuota val vuota: List[Nothing] = List()

val frutta = "uva" :: ("anguria" :: ("pesca" :: Nil)) val numeri = 1 :: (2 :: (3 :: (4 :: Nil))) val vuota = Nil

val frutta1 = "uva" :: ("anguria" :: ("pesca" :: Nil)) val frutta2 = "melone" :: ("mela" :: ("banana" :: Nil)) var fruit = frutta1 ::: frutta2 frutta = frutta1.:::(frutta2) frutti = List.concat(frutta1, frutta2)

val lista = List[tipo]

Page 27: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

27

Altre funzioni sono lenght() che ritorna il numero di elementi appartenenti alla lista oppure

reverse() che inverte l’ordine di posizione degli elementi all’interno della lista.

3.4.3 Vettori

L’utilizzo di una lista è molto efficiente quando bisogna lavorare con algoritmi che

prelevano solo elementi in testa dato che il loro accesso, aggiunta o rimozione impiega

sempre un tempo costante. Invece quando gli elementi da modificare o accedere non sono

in testa il tempo di esecuzione aumenta in modo lineare in base alla loro posizione

all’interno della lista. Per ovviare all’inefficienza dell’accesso casuale nelle liste è stata

introdotta la struttura dati vettore che permette di accedere ad ogni suo elemento in un

tempo costante indipendentemente dalla sua posizione. Si presenta la sintassi da utilizzare

per creare un nuovo vettore:

Per poter aggiungere elementi in testa si utilizza l’operatore +:

Invece per aggiungere elementi in coda si fa uso dell’operatore :+

Essendo i vettori una struttura dati immutabile non è possibile modificare un elemento di

un vettore già creato ma, invece, è possibile utilizzare il metodo updated per creare un

nuovo vettore con un solo elemento modificato:

Tramite quest’operazione abbiamo creato un nuovo vettore contenente i seguenti valori

1,2,3,4,0 senza modificare il vettore originale.

val vettore = Vector(23,24)

val vettore2 = 3 +: vettore

val vettore3 = vettore2 :+ 4

val vettore = Vector(1,2,3,4,5) val vettore2 = vettore updated(4,0)

Page 28: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

28

3.4.4 Collezioni parallele

Le collezioni parallele sono state introdotte in Scala nel tentativo di facilitare la

programmazione parallela evitando agli sviluppatori di conoscere i dettagli di basso livello

della parallelizzazione e nel frattempo fornendo loro un’astrazione di alto livello semplice

e familiare. È chiaro, quindi, che l’uso di tale struttura dati è quello di sfruttare, dove

possibile, le capacità multi-threading delle moderne architetture hardware. Scala

ricorsivamente suddivide la collezione in tante partizioni applicando in parallelo ad ogni

partizione un’operazione ed infine unisce tutti i risultati delle operazioni effettuate.

La creazione di una collezione parallela avviene utilizzando la seguente sintassi con

l’obbligo dell’importazione della classe ParVector:

3.4.5 Mappe

Le mappe sono strutture dati utili per salvare un insieme di coppie chiave/valore. Le chiavi

sono uniche all’interno della mappa e ogni valore può essere recuperato in base alla sua

chiave. Esistono due tipi di mappe: immutabili e mutabili e differiscono semplicemente

perché le mappe immutabili non possono essere modificate una volta create. La sintassi

per poter creare una mappa è la seguente:

Scala istanzia di default mappe immutabili e affinché sia possibile creare mappe mutabili è

necessario importare la classe “scala.collection.mutable.Map”. Se si ha la necessità di

utilizzarle entrambe si possono creare mappe immutabili tramite la parola chiave Map

mentre per quelle mutabili la parola chiave mutable.Map.

import scala.collection.parallel.immutable.ParVector val p1 = ParVector(1, 2, 3, 4, 5, 6, 7, 8)

val provincia = Map( “Campania” -> List(“Napoli”, “Benevento”, “Avellino”, “Salerno”, “Caserta”), “Lazio” -> List(“Roma”, “Latina”, “Frosinone”, “Rieti”, “Viterbo”), “Valle d’Aosta” -> List(“Aosta”) )

Page 29: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

29

Le operazioni base che fornisce tale struttura dati sono:

keys: ritorna tutte le chiavi della mappa.

values: ritorna tutti i valori della mappa.

3.5 Cenni sulla programmazione concorrente

Con la diffusione sempre maggiore di processori multi-core è diventato indispensabile

utilizzare un tipo di programmazione orientato alla gestione della concorrenza. Sin dalle

prime versioni Scala ha adoperato il modello degli attori come soluzione alla

programmazione concorrente che si basa principalmente sullo scambio di messaggi. Dalla

versione 2.11 il sistema interno adoperato da Scala è stato sostituito dal toolkit Akka. Tale

modello prevede un alto livello di astrazione semplificando il lavoro dello sviluppatore in

quanto non deve più gestire in modo esplicito thread e lock. Ogni attore possiede delle

variabili che riflettono lo stato in cui si trova e rappresentano dati importanti che non

devono essere corrotti dall’interazione con altri attori. Per questo motivo, a protezione di

tali stati, Akka prevede che ogni attore abbia un proprio thread, completamente schermato

dal resto del sistema, in modo tale che non bisogna preoccuparsi di sincronizzare l’accesso

tramite lock. Ogni volta che un attore riceve un messaggio decide se processare il

messaggio, nel caso in cui il client è autorizzato, oppure ignorarlo in caso contrario.

Affinché sia possibile gestire la coda messaggi ogni attore possiede una mailbox che può

essere implementata con logica FIFO (impostazione di default) oppure con logica di

priorità. Ogni attore può creare degli attori-figli a cui può delegare dei sotto-task. La

creazione ed eliminazione degli attori-figli avviene in modo asincrono in modo da non

bloccare l’esecuzione dell’attore-padre. Quando un attore crea un attore-figlio

automaticamente diventa un suo supervisore e in quanto tale ha il compito di gestire gli

errori che vengono rilevati dall’attore-figlio scegliendo una tra le seguenti opzioni:

Riprendere l’attore-figlio conservando il suo stato interno.

Riavviare l’attore-figlio cancellando il suo stato interno.

Arrestare definitivamente l’attore-figlio.

Infine quando un attore completa le sue operazioni oppure viene terminato libera tutte le

Page 30: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

30

sue risorse e le lettere non processate all’interno della mailbox vengono inviate

all’EventStream come DeadLetters. Tali lettere sono utilizzate per scoprire le cause di un

eventuale crash di sistema.

Page 31: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

31

Appendice: Esempi di codice

Client – Server

Codice relativo al server che apre una connessione sulla porta 19999, salva attraverso un

buffer il messaggio ricevuto dal client e lo stampa. Se il messaggio ricevuto dal client

contiene la parola “Disconnetti” la connessione è terminata.

import java.net._ import java.io._ import scala.io._ object Server extends App { try { val server = new ServerSocket(19999) println("Server inizializzato:") val client = server.accept val in = new BufferedReader(new InputStreamReader(client.getInputStream)).readLine val out = new PrintStream(client.getOutputStream) println("Server ricevuto:" + in) out.println("Messaggio ricevuto") out.flush if (in.equals("Disconnetti")) client.close; server.close; println("Il Server sta terminando la connessione:") } catch { case e: Exception => println(e.getStackTrace); System.exit(1) } }

Page 32: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

32

Invece di seguito è mostrato il codice del client nel quale viene creata una finestra grafica

di dimensione 500x500 contenente due bottoni: Invia che serve per inviare un messaggio

al server con la parola “Hello” mentre Disconnetti termina la connessione.

import java.net._ import java.io._ import scala.io._ import swing._ import Swing._ object MyClient extends MainFrame with App { title = "Client" preferredSize = (500, 500) val socket = new Socket(InetAddress.getByName("localhost"), 19999) var in = new BufferedSource(socket.getInputStream).getLines val out = new PrintStream(socket.getOutputStream) println("Client inizializzato:") contents = new BorderPanel { add(new FlowPanel { contents += new Button(new Action("Invia") { def apply { out.println("Hello!") out.flush println("Client received: " + in.next) } }) contents += new Button(new Action("Disconnetti") { def apply { out.println("Disconnetti") out.flush socket.close } }) }, BorderPanel.Position.Center) } pack visible = true }

Page 33: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

33

Conteggio parole

Di seguito è mostrato il codice di un semplice programma che conta il numero di parole

che si trovano all’interno di una frase definito ed eseguito tramite shell interattiva.

scala> import scala.collection.mutable import scala.collection.mutable scala> def countWords(text: String) = { | val counts = mutable.Map.empty[String, Int] | for (rawWord <- text.split("[ ,!.]+")) { | val word = rawWord.toLowerCase | val oldCount = | if (counts.contains(word)) counts(word) | else 0 | counts += (word -> (oldCount + 1)) | } | counts | } countWords: (String)scala.collection.mutable.Map[String,Int] scala> countWords("See Spot Run! Run, Spot. Run!") res30: scala.collection.mutable.Map[String,Int] = Map(see -> 1, run -> 3, spot -> 2)

Page 34: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

34

Conclusioni

Attraverso l’elaborato è stato possibile conoscere l’evoluzione e, soprattutto, le

caratteristiche principali che offrono i linguaggi funzionali, garantendo una migliore

gestione della modularità rispetto ai linguaggi imperativi. Successivamente è stata fatta

un’analisi dettagliata di quali sono la sintassi e le strutture che costituiscono Scala. Come è

già stato notato durante l’elaborato, Scala si presenta come un linguaggio a paradigma

misto offrendo, quindi, agli sviluppatori le caratteristiche e i vantaggi sia della

programmazione ad oggetti che di quella funzionale. In questo modo, amalgamando le

varie tecniche, garantisce un maggiore aiuto nella risoluzione di problemi che si possono

incontrare. Grazie alle caratteristiche della programmazione funzionale è possibile

migliorare la stabilità del software e ridurre i problemi che nascono da effetti collaterali

indesiderati. Utilizzando strutture dati immutabili rispetto a quelle mutabili e scegliendo

funzioni pure che non interferiscono dannosamente con l’ambiente di sviluppo è possibile

scrivere codice molto più sicuro, affidabile e facile da comprendere. Caratteristica da non

sottovalutare è l’esecuzione di Scala sulla JVM garantendo massima compatibilità con la

maggior parte dei dispositivi che si trovano in commercio. Inoltre permette di importare

nei propri progetti tutte le librerie e plugin sviluppati appositamente per Java offrendo, in

questo modo, le stesse potenzialità di Java ma ampliandole con le caratteristiche uniche di

Scala. Un altro vantaggio molto apprezzato e utilizzato dagli sviluppatori, soprattutto lato

server, è la gestione ottimale della concorrenza grazie al modello degli attori di cui è stato

fatto un cenno nell’elaborato. Le note negative che si possono riscontrare in tale

linguaggio sono da ricercare in un IDE non ancora molto performante nel caso di software

che hanno molte dipendenze che interagiscono tra loro e, infine, in una curva

Page 35: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

35

d’apprendimento un po’ lenta a causa della complessità dovuta all’insieme di tecniche

avanzate appartenenti a due diversi tipologie di linguaggi.

Page 36: Caratteristiche fondamentali del linguaggio di ... · Anno Accademico 2015/2016 Candidato: Davide Casillo matr. N46001379 [Dedica] Indice ... linguaggio LISP come ad esempio Scheme

36

Bibliografia

[1] Martin Odersky, Lex Spoon, and Bill Venners, Programming in Scala, 2008.

[2] John Hughes, Why Functional Programming Matters, 1990.

[3] Alvin Alexander, Scala Cookbook, 2013.

[4] Scala Documentation,

http://www.scala-lang.org/documentation/, 4/09/16

[5] Tutorials Point,

http://www.tutorialspoint.com/scala/scala_overview.htm, 01/09/16.

[6] Haskell Wiki,

https://wiki.haskell.org/Functional_programming, 28/07/16

[7] Wikipedia, Scala Programming Language,

https://en.wikipedia.org/wiki/Scala_(programming_language), 2/09/16

[8] Wikipedia Italia, Scala (linguaggio di programmazione),

https://it.wikipedia.org/wiki/Scala_(linguaggio_di_programmazione), 2/09/16

[9] Akka Documentation,

http://doc.akka.io/docs/akka/2.4/scala.html, 3/09/16

[10] Wikipedia, Akka (toolkit),

https://en.wikipedia.org/wiki/Akka_(toolkit), 3/09/16

[11] History of Functional Languages,

http://caml.inria.fr/pub/docs/fpcl/fpcl-02.pdf, 25/07/16

[12] Courseware on Functional Programming,

http://athena.ecs.csus.edu/~csc135fp/project/history.html, 25/07/16