Decomposizione funzionale

Lo scopo dei metodi di classificazione e di accesso, e in un modo o nell’altro anche delle altre soluzioni al problema della decomposizione viste finora, è sostanzialmente quello di invertire il processo di costruzione degli oggetti:

In altre parole, tutti gli elementi necessari a classificare un oggetto e ad accedere alle informazioni che esso contiene sono fornite dalla costruzione dell’oggetto, ovvero dal valore che descrive l’oggetto nel modello di sostituzione (dato che la rappresentazione di un oggetto in tale modello corrisponde appunto all’invocazione del costruttore). Il pattern matching è il meccanismo tramite il quale i linguaggi funzionali consentono di utilizzare le informazioni specificate in tale valore.

Classi case

L’implementazione del pattern matching in Scala è fortemente influenzata dal fatto che Scala sia un linguaggio orientato agli oggetti: il pattern matching agisce principalmente su un tipo particolare di classi, le classi case.

La definizione di una classe case (case class) è analoga alla definizione di una classe normale, ma è preceduta dalla parola riservata case. Ad esempio, le definizioni

trait Expr
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr
case class Prod(e1: Expr, e2: Expr) extends Expr
case class Var(name: String) extends Expr

realizzano la gerarchia delle espressioni aritmetiche, che è costrituita dal trait Expr e da quattro classi concrete che estendono il trait. Siccome il pattern matching opera sulle istanze delle classi, tipicamente si definiscono con case solo le classi concrete (non astratte) di una gerarchia (e case non è proprio ammesso nella definizione dei trait).

Come effetto della definizione di una classe case il compilatore fornisce automaticamente diversi supporti sintattici:

Oltre che per le classi, la parola riservata case può essere utlizzata anche nella definizione degli object (in tal caso, tra i supporti sintattici appena elencati il compilatore fornisce solo quelli che hanno senso per gli oggetti singleton — ad esempio non vengono forniti nè un metodo factory nè un metodo copy, perchè non si possono costruire o copiare istanze di un singleton object).

Companion object con un metodo factory

Per ogni case class il compilatore definisce implicitamente un companion object con un metodo factory apply che invoca il costruttore primario della classe e ha la stessa segnatura di tale costuttore. Tale metodo può poi essere usato (come già visto nell’implementazione di List presentata in precedenza) per costruire istanze della classe senza bisogno di scrivere l’operatore new.

Ad esempio, nel caso della gerarchia di Expr riportata sopra vengono implicitamente definiti i compaion object