Stairway to the stars (Guida introduttiva a Scala)

Da Java a Scala

ScalaOrmai da anni programmo in Java e devo dire che è un linguaggio che, dopo le prime fasi di difficoltà e frustrazione, sa dare tante soddisfazioni. E’ vasto, potente, offre maree di librerie, usato bene è come la bacchetta di Harry Potter. Però diciamocelo: anche i più appassionati Javisti non potranno negare che a volte ci si trova a gestire alcune situazioni in modo bizzarro, altre in modo macchinoso o complesso.

Per questo motivo periodicamente mi guardo intorno spinto dalla domanda: c’è qualcosa di nuovo? Infatti bisogna tener presente che Java compirà vent’anni il prossimo anno e nel frattempo si è scoperto che il paradigma di programmazione orientato agli oggetti non è la panacea che si credeva.

Caccia al tesoro

In questi ultimi mesi ho trovato due linguaggi che hanno attirato la mia attenzione: Groovy e Scala. Entrambi presentano caratteristiche molto interessanti, tra cui:

  • Estendono Java, arricchendola di costrutti aggiuntivi;
  • Aggiungono aspetti funzionali a quelli object oriented, rendendo il codice necessario per compiere alcuni task più coinciso ma espressivo;
  • Si integrano in modo trasparente con le classi e le librerie Java esistenti;
  • Si compilano in bytecode Java, quindi girano sulla JVM e possono essere usati ovunque si usa Java;
  • Possiedono plugin per i più diffusi IDE (Eclipse, Netbeans e Idea IntelliJ).

Salendo la Scala per mano a Java

Alla fine ho deciso di approfondire il linguaggio Scala, per il semplice motivo che Scala, a differenza di Groovy, rimane comunque un linguaggio strongly typed e per me, che sono un tipo da mutuo a tasso fisso, la cosa è molto rassicurante. Passo quindi a condividere con i miei venticinque lettori quello che ho imparato finora.

Cos’ è Scala

Per chi è un Javista (ce n’è uno anche nelle migliori famiglie) Scala possiede una sintassi molto simile e concetti analoghi, con qualche differenza importante. Scala è:

  • object oriented: ogni cosa è un oggetto. I tipi e i comportamenti degli oggetti sono descritti da classi e tratti. I tratti sono l’analogo delle interfacce Java, ma possono avere anche dei metodi implementati, agevolando il riutilizzo del codice
  • funzionale: in Java non esistono funzioni che non siano incluse in un oggetto, ovvero che non siano metodi, Scala consente di creare funzioni vere e proprie e di utilizzare le closure
  • strongly typed: come Java questo linguaggio costringe il programmatore a utilizzare variabili fortemente tipate, in modo da fornire una alta probabilità che molti errori saranno individuati in fase di compilazione e non a runtime. Questo non deve implicare rigidità (come in Java sono presenti i Generics) nè dover scrivere codice inutile: in Scala esiste un sofisticato sistema di local type inference che consente allo sviluppatore di non specificare il tipo di variabili e parametri quando il compilatore può evincerli da soli.

Queste caratteristiche danno vita ad un linguaggio elegante ma potente, sicuro ma flessibile, chiaro ma coinciso e che può integrarsi perfettamente nel codice Java preesistente. Molti dicono che Scala combina il meglio di Java e Ruby (il linguaggio di programmazione, eh!): insomma, viene davvero voglia di provarlo, no?

Da dove parto

Cominciare a programmare Scala è facile: basta scaricarlo dal sito ufficiale. Ovviamente andremo a scrivere codice Scala da dentro un IDE e in questo caso abbiamo l’imbarazzo della scelta: Netbeans, Eclipse o IntelliJ! Le istruzioni per installare il plugin nelle sue diverse versioni sono disponibili alla pagina IDE and Editor plugins.

Vediamo un po’ di codice, dai!

HelloWorld! Singleton

Come da antica tradizione partiamo con un HelloWorld!:

object HelloWorld
{
  def main(args: Array[String])
     {
        println("Hello, world!")
     }
}

Beh, ci sarebbero già tante cose da dire. Una classe Scala si definisce con class, ma con object definiamo un singleton! Scala fa piazza pulita di metodi e classi statiche, perché filosoficamente vanno contro il principio di scope ridotto e information hiding dei linguaggi Object Oriented; quindi per realizzare un tipo da usare in singola istanza si usa object. Notiamo inoltre che:

  1. il punto e virgola a fine frase non compare più: in Scala diventa opzionale, è necessario solo nel caso in cui vengano inserire più istruzioni nella stessa riga;
  2. per definire un metodo o una funzione si utilizza la parola chiavedef.

HelloWorld! da linea di comando

Scala consente anche di lanciare al volo comandi da linea di comando. Se avete installato Scala potete avviare il prompt (su Windows) o un terminale (su Linux) e digitare scala:


Scala su terminale

A questo punto l’Hello World! diventa facile come scrivere println("Hello World!"):

Hello Scala su terminale

Any?

In Java ogni oggetto ne estende un altro: dove non esplicitamente dichiarato stiamo estendendo Object (ogni cosa è un oggetto!). In Scala è la stessa cosa, ma al vertice della gerarchia di tipi c’è Any: ne parleremo ancora tra poco.

Via i tipi primitivi

I tipi base di Scala sono gli stessi di Java: Boolean, String, Int, Double e così via. Sparisce la barocca differenza tra tipi primitivi (quelli minuscoli) e quelli incapsulati (esempio: int e Integer) con la necessità di boxing e unboxing. In Scala i tipi base sono anch’essi oggetti: è assolutamente valido scrivere:

object HelloWorld {
  def main(args : Array[String])  = Console.println("Hello my " + 25.toString() + " readers")
}

ovvero il numero 25 ha il toString perché è in realtà un oggetto!

I tipi base di Scala estendono AnyVal, a sua volta sottoclasse di Any. A questo punto forse un bel diagramma con le gerarchie dei tipi di Scala può aiutare:


class hierachy

Al vertice della gerarchia, come dicevamo, c’è Any. Questo tipo è esteso da due sottotipi: AnyVal ed AnyRef, che dividono il mondo dei tipi Scala in due: classi valore e classi riferimento. Le classi valore sono i tipi predefiniti, in Java i tipi primitivi. Tutte le altre classi definiscono tipi riferimento. I tipi riferimento si dividono a loro volta in: classi Scala, tra cui quelle definite dall’utente, che estendono ScalaObject, e classi del Java runtime environment, che estendono AnyRef (che quindi va a corrispondere a java.lang.Object).

Ancora vivi? Bene, adesso ci rinfranchiamo con un po’ di codice!

Una classe d’esempio

Vediamo come si scrive una classe Point che ha due proprietà, x ed y, e due metodi, move e translate:

class Point(var x: Int, var y: Int) {
   def this() = this(0,0)
   def move(x1:Int, y1:Int) = {x=x1; y=y1}
   def translate(dx: Int, dy: Int) = {
      x += dx; y += dy;
   }
override def toString = "("+x+","+y+")"
}

La classe inoltre ridefinisce il toString che eredita da AnyRef. Notiamo che la firma dei metodi è così costituita:

def nome_metodo(parametri) : tipo_ritorno = { corpo_del_metodo }

ma in realtà Scala è molto furbino e quindi:

  • Il tipo di ritorno del metodo può essere omesso se è inferibile: Scala desume che sia il valore dell’espressione dell’ultima riga del corpo del metodo
  • Quando il metodo non ritorna nessun valore Scala ritorna Unit (che sostitusce il void di Java)
  • La funzione viene definita con = seguito da blocco di codice (delimitato da { e }). Se il corpo della funzione è composto da una sola istruzione le parentesi graffe possono essere omesse

Guarda mamma, senza cicli!

Vi lascio con quest’ultimo snippet di codice: supponiamo di avere una lista che contiene i primi 8 numeri naturali:

// il tipo lo metto solo a destra, metterlo a sinistra è ridondante
val l = List( 1, 2, 3, 4, 5, 6, 7, 8 ) 

val serve per dichiarare una variabile immutabile: la parola chiave final non esiste più in Scala. Adesso questa lista voglio filtrarla, lasciando solo i numeri dispari. In Java dovrei:

  1. istanziare una lista result vuota;
  2. ciclare la lista l: ogni elemento i viene incluso in result se è dispari;
  3. terminata l’iterazione della lista l restituire result.

In Scala invece la cosa è molto rapida:

// l2 contiene 1, 3, 5, 7
val l2 = l.filter(x => x % 2 == 1)

La funzione filter accetta come parametro una funzione; il tipo della funzione (sì, le funzioni sono in realtà un oggetto e quindi hanno un tipo!) è: un parametro di input del tipo contenuto nella lista, un parametro di output di tipo Boolean. La funzione può essere definita al volo in modo anonimo dentro alle parentesi di filter: in questo caso se x modulo 2 è uguale ad 1 restituisce true, altrimenti false.

Faccio notare che la lista di partenza l non viene modificata da filter. Tutte le collection in Scala sono immutabili (come String in Java). Questo garantisce codice più sicuro in caso di concorrenza e spesso performance maggiori.

Conclusioni

Scala è un linguaggio affascinante: potente e flessibile, che sembra racchiudere il meglio di diversi linguaggi. In questo post abbiamo solo salito i primi gradini di Scala, ma ovviamente ci sarebbero ancora molte rampe davanti a noi. Spero che avremo modo nei prossimi post di salire ulteriormente assieme.

E voi cosa ne pensate? Serviva un linguaggio come Scala oppure Java bastava? Che impressione vi ha fatto? Pensate di provare un Hello World! in Scala oppure no? Sono curioso di sentire le vostre impressioni!

A presto!

Manuele Piastra

Sono uno Scrum Master e Project Manager i cui skill tecnici sono focalizzati al momento sullo sviluppo di applicazioni Java EE su IBM Websphere 7.0 utilizzando JSF (RichFaces), JPA (EclipseLink) ed EJB3. Presso OmniaGroup ricopro il ruolo di Training Manager: seleziono il personale tecnico, mi occupo della sua crescita formativa organizzando Corsi e Workshop sia interni che esterni, molti dei quali hanno visto me come docente. LinkedIn Profile - Google+

  • Simone Pavan

    Sinceramente, avendo fatto conoscenza di altri linguaggi funzionali in particolare CaML, la inferenza di tipo in Scala potrebbe essere migliorata. Ad Esempio non capisco il motivo per cui esista l’obbligo di specificare il tipo di parametro negli argomenti delle funzioni. Ad esempio:
    def fact(x:Int):Int = if x == 1 return 1;else return x*fact(x-1);
    in CaML non ci sarebbe stato nessun bisogno di dicharare x di tipo intero, l’avrebbe semplicemente inferito dalle operazioni svolte nel corpo della funzione.
    Probabilmente il fatto che in Scala è tutto un oggetto non aiuta il compilatore a inferire i tipi, peccato perchè in questo modo Scala perde la tipica eleganza dei linguaggi funzionali.

    • Ciao,
      innanzitutto grazie per averci seguito e per il tuo gradito commento. Io provengo da un background molto object oriented e gli aspetti funzionali di Scala per me erano già notevoli :-). Però hai ragione: l’inferenza dei tipi può essere ancora migliorata! Interessante questo linguaggio, CaML, che non conosco. Spero un giorno di poterlo utilizzare! Saluti

      • Simone Pavan

        Ciao in realtà sono uno sviluppatore java anch’io,
        ho consciuto CaML all’università e mi è piaciuto subito per la sua eleganza e perchè è molto “stringato” (che non significa che fa un uso esagerato di stringhe :))
        esiste anche la versione a oggetti OCaML.
        In verità non è un linguaggio funzionale puro perchè usa anch’esso dei costrutti imperativi (es. cicli while e for) che, ma nella maggior parte dei casi, possono essere elegantemente
        sostituiti con la “tail recursion”.
        La stessa funzione sarebbe scritta in questo modo:

        let rec fact x = if x=1 then 1 else x*f(x-1)

        se vuoi provalo è divertente :=) da linux sudo apt-get install ocaml

        Come puoi notare non è stata dichiarata la variabile x e nemmeno il tipo
        di ritorno di fact.

        Ritornando a Scala comprendo benissimo la difficoltà nello scrivere
        il controllo sintattico e semantico, il simbolo * in quel contesto
        può solamente essere usato per i numeri ma interi o double ?
        CaML è aiutato dall’operatore punto,  * moltiplica interi mentre *. moltiplica float

        Detto questo continuerò a seguire i prossimi post su Scala perchè credo che lo userò,
        e poi perchè… non si è mai finito di imparare 🙂

        Ciao.

  • Pingback: Le Google Guava Libraries: mini tutorial da 10 minuti | Cose Non Javiste()

  • Mark Tosini

    Interessante……molto!!
    Inizierò a giocarci un pò!!
    Java è un ottimo linguaggio ma gli anni passano per tutti!!!!

  • Pingback: Il Java che verrà: una breve introduzione alle espressioni lambda | Cose Non Javiste()

  • Pingback: ScalaDay Italy 2013, la prima conferenza italiana su Scala | Cose Non Javiste()

  • rubycommenter

    Non mi aspettavo Scala avesse preso così tante (piccole e grandi) idee da Ruby! E inoltre avvicinandomi alla comprensione dei linguaggi funzionali è bello scoprire di aver già scritto codice che utilizza paradigmi funzionali in Ruby senza saperlo! 🙂