lunedì 31 marzo 2008

Scala e i suoi fratelli

Da qualche tempo guardo con interesse ai nuovi linguaggi che fanno la loro comparsa nel variegato orizzonte della programmazione dei computer. Tra gli altri, soprattutto Scala.

Giusto due note a proposito: che sono, lo sottolineo, del tutto soggettive.

La prima: il paradigma della programmazione funzionale non soppiantera' il paradigma a oggetti. Infatti il successo della programmazione a oggetti deriva dal fatto che l'essere umano,normalmente, pensa per oggetti e relazioni tra oggetti, e non per funzioni. Tranne casi particolari (il matematico, il fisico) l'uomo non pensa a un problema in termini funzionali (e men che meno di funzione di funzione, o di funzione di funzione di funzione...)

La seconda: dopo Java (e C#) il panorama dei linguaggi per computer e' abbastanza statico, e ho l'impressione che i nuovi venuti non portino significativo valore aggiunto. Ho l'impressione che oggi il focus si debba spostare dai linguaggi alle API. Il successo di Java, e poi del .NET Framework, si deve all'efficacia e all'ampiezza delle loro API piuttosto che delle specifiche tecniche del linguaggio. Francamente, al programmatore medio importa assai poco delle closures piuttosto che delle list comprehensions. Se invece riesce a creare un file e poi zipparlo in due linee di codice il discorso cambia...

Inoltre, il sempre maggior peso della UI (sia lato desktop che lato Web, il che ho l'impressione che prossimamente sara' la stessa cosa...) privilegia quei linguaggi e piattaforme che permettono di realizzare facilmente le interfacce grafiche e di integrarle con la business logic dell'applicazione.

In questo senso, il .NET Framework con il suo supporto a Silverlight attraverso XAML e il WPF, e dall'altra parte Adobe con AIR/Flex per Flash, siano i veri nuovi protagonisti della scena.

mercoledì 5 marzo 2008

Se starnazza come un'anatra...

... allora è un'anatra!

No, non sono diventato un membro del club degli ammiratori di Massimo Catalano (ricordate "Quelli della notte"?). E' solo un modo per esprimere un concetto che differenzia i linguaggi a oggetti di scripting da quelli compilati. I primi genericamente sono blandamente tipizzati, i secondi invece fortemente tipizzati. Questo impatta sul discorso delle interfacce che facevo in precedenza: nel primo caso (Ruby o Python, ad esempio) il fatto che io esprima un'interfaccia dei miei oggetti è solamente un aiuto che do a chi legge il mio programma: gli comunico esplicitamente come ho pensato il design a oggetti della mia soluzione. Nel secondo caso (Java o C#, ad esempio), senza un'interfaccia, il metodo polimorfico semplicemente non funziona.

Nel secondo caso, per tornare alle anatre, bisogna dire al compilatore: guarda che ti sto passando un'anatra, comportati di conseguenza! Nel primo, all'interprete, non dico niente: se starnazza come un'anatra, allora è un'anatra. Punto e basta.

Un esempio polimorfico in Java e in Ruby ci può aiutare a capire meglio. Cominciamo da un linguaggio fortemente tipizzato come Java:


package anatre;

import java.util.ArrayList;
import java.util.Collection;

interface IGallinaceo
{
String starnazza();
}

public class Anatra implements IGallinaceo
{
@Override
public String starnazza()
{
return "Sono un'anatra e starnazzo!";
}
}

public class Oca implements IGallinaceo
{
@Override
public String starnazza()
{
return "Sono un'oca e starnazzo!";
}
}


public final class Cortile
{

public static void main(String[] args)
{
Collection<IGallinaceo> cortile =
new ArrayList<IGallinaceo>();

cortile.add(new Anatra());
cortile.add(new Oca());
cortile.add(new Anatra());

for (IGallinaceo gall : cortile)
{
System.out.println(gall.starnazza());
}
}
}



Come si vede, le due classi Ocae Anatra implementano esplicitamente l'interfaccia IGallinaceo, cioè sono (IS-A) a tutti gli effetti dei Gallinacei e il compilatore lo sa a compile-time.

Vediamo lo stesso esempio in Ruby.


class Oca
def starnazza
return "Sono un'oca e starnazzo";
end
end

class Anatra
def starnazza
return "Sono un'anatra e starnazzo"
end
end

cortile = Array.new
cortile.push(Anatra.new)
cortile.push(Oca.new)
cortile.push(Anatra.new)

for animale in cortile
puts(animale.starnazza)
end


Qui semplicemente io passo una serie di classi al mio metodo polimorfico, e mi limito a richiamare il metodo "starnazza". Se facessi questa cosa in Java avrei un errore di compilazione! Invece, all'interprete Ruby non interessa che io "affermi" che un'Anatra è (IS-A) un Gallinaceo: se starnazza, allora lo è. Si fida! (Beato lui). E se gli passo un oggetto che non implementa "starnazza"? Be', semplicemente avrò un errore a run-time.

Si tratta dunque di due filosofie diverse: nella prima, io devo dichiarare sempre la classe dell'oggetto passato a un metodo, nella seconda posso farne a meno. Non c'è chiaramente una soluzione migliore dell'altra: nel primo caso privilegio le performance, e nel secondo la flessibilità del design.

(E se avete dei dubbi sul fatto che Oche e Anatre starnazzino, date un'occhiata qui!)