Affinita' e divergenze tra il polimorfismo e le funzioni come oggetti di prima classe

Saturday, 30 December 06
polimorfismo Persino un programmatore che ha sempre e solo utilizzato l'assembler come "linguaggio" di programmazione sa cosa sia una funzione (magari l'ha chiamata routine, ma cosa importa?).

La funzione e' il mezzo tramite il quale astrai le operazioni ripetitive di un programma. Ma aspetta, questo modo di vedere la funzione e' riduttivo.

Cio' che la funzione astrae e' la parte interna del programma, la polpa: Se devi astrarre la buccia le funzioni classiche, quelle con un nome, che non possono essere passate come argomento, non ti aiutano piu'.

Quando le funzioni funzionano

Ammettiamo di avere il seguente programma in pseudo-codice:
function validateUserPass(user,pass) {
    if (user.len < 5 || user.hasBadChars) return false;
    if (pass.len < 5 || user.hasBadChars) return false; 
    return true;
}
La funzione controlla che user e pass siano valide (non piu' corte di 5 caratteri e non contengono caratteri "cattivi"). Ma utilizzando una funzione potremmo evitare di scrivere le stesse cose due volte (o molte di piu', immaginiamo che questo controllo venga utilizzato molte volte). Cosi' scriviamo:
function validateField(f) {
    if (f.len < 5 || f.hasBadChars) return false;
    return true;
}

function validateUserPass(user,pass) { if (validateField(user) && validateField(pass)) return true; return false; }
Il concetto di campo valido e' stato spostato dalla funzione originale ad una separata. Ora per il programmatore il concetto di campo valido e' diventato astratto, non si deve piu' curare di come funziona, lo usa e basta.

Come detto pero', abbiamo cambiato un pezzo di polpa del programma. Immaginate il programma come un albero che ha via via rami piu' sottili. In questo caso i rami piu' sottili si ripetevano, e abbiamo fatto in modo di astrarli invece che crearli ogni volta che vogliamo ricostruire l'albero. Ma a volte le cose vanno in maniera diversa. Sono i rami piu' grossi che sono uguali e vorremmo astrarre! la scorza di un algoritmo che e' sempre uguale, mentre invece quello che cambia sono i rami piu' sottili.

Un esempio classico di questo problema e' la paginazione di dati in HTML.

In una applicazione web complessa e' necessario fare la paginazione di dati innumerevoli volte. Il concetto di paginazione e' sempre uguale dal punto di vista della logica con cui funziona:
  • Ottieni gli elementi in un dato intervallo
  • Visualizzali uno dopo l'altro nella pagina
  • Crea una barra di navigazione con il numero di pagine
Cio' che cambia da una paginazione all'altra non e' questo concetto esterno, ovvero questa scorza dell'algoritmo, questi rami grandi, ma quelli piu' sottili, ovvero: Quali elementi ottenere? Come vengono visualizzati? Se sono utenti devo fare una cosa, se sono elementi di una TODO list devo fare qualcosa di diverso, e ancora una cosa diversa se sono risultati di un motore di ricerca.

Eppure cio' che il programma fa e' sempre la stessa cosa in teoria. Ma magari a volte vi siete ritrovati ad avere nella stessa applicazione innumerevoli pezzi di programma che si assomigliano molto tra di loro perche' fanno la paginazione di questo o di quello.

La cura

Qualunque potente linugaggio di programmazione puo' risolvere questo problema, ma in questo articolo mi preme far notare come il polimorfismo (un concetto della programmazione orientata agli oggetti) e le funzioni come oggetti di prima classe (un concetto della programmazione funzionale) siano piu' o meno equivalenti nel modo in cui risolvono il problema, anche se ognuno di questi approcci ha meriti e demeriti.

Utilizzando la OOP per la paginazione si potrebbe scrivere la seguente funzione:
function listItems(start,items) {
    itemsList = items.getRangeOfItems(start,start+items.perPage);
    itemsCount = items.getNumerOfItems();
    foreach(itemsList as item) {
        items.RenderAsHtml(item);
    }

....

/* codice per creare una barra di navigazione utilizzando il fatto che conosciamo sia il numero di items per pagina (items.perPage) che il numero totale di items (itemsCount). */

....

}
Basta passare a listItems un oggetto 'items' che esporta la stessa interfaccia (gli stessi metodi e attributi insomma) una volta per le news, una per gli utenti, una per altre cose ancora ed e' fatta. Possiamo creare liste di cose con la paginazione senza riscrivere ogni volta l'algoritmo.

Lo stesso codice puo' essere anche scritto in maniera funzionale. Cosa accade infatti se utilizzo un linguaggio in cui le funzioni possono accettare come argomenti altre funzioni? Posso passare i "metodi" come funzioni e il gioco e' fatto:
function listItems(start,perPage,getRange,getNum,renderAsHtml) {
    itemsList = getRange(start,start+perPage);
    itemsCount = getNum();
    foreach(itemsList as item) {
        RenderAsHtml(item);
    }

....

/* codice per creare una barra di navigazione utilizzando il fatto che conosciamo sia il numero di items per pagina (perPage) che il numero totale di items (itemsCount). */

....

}
I due approcci sono nella sostanza molti simili. In realta' l'esempio potrebbe essere di molto migliorato nella pratica. Ad esempio le funzioni che ritornano gli items e il numero totale di items potrebbero essere una sola che torna un array associativo con le due informazioni (o un oggetto con due attributi).

Inoltre un parametro "filtro" e' praticamente indispensabile nella pratica. Il filtro sara' passato alla funzione che si occupa di ritornare gli elementi della lista e il loro numero, e puo' essere qualunque cosa e' adatta nel contesto di programmazione. Ad esempio un pezzo di SQL come "WHERE time < ..." o una funzione che si occupa di filtrare i contenuti che vogliamo visualizzare da quelli che non vogliamo che appaiano in un data lista. Il bello e' che non importa di preciso cosa sia il filtro dal punto di vista della funzione listItems(), che si occupa solo di passare il filtro alle funzioni che poi fanno il lavoro sporco.

Questo post e' gia' troppo lungo, cosi' nel prossimo mostrero' una implementazione reale in PHP di questo concetto, e affrontero' le differenze pratiche dei due approcci nel caso in cui il linguaggio utilizzato e' molto limitaito sia dal punto di vista dell'approccio funzionale che di quello OOP (PHP4 e' limitatissimo in tutti e due i casi).

Buon anno!
3104 views*
Posted at 07:13:16 | permalink | 4 comments | print
Do you like this article?
Subscribe to the RSS feed of this blog or use the newsletter service in order to receive a notification every time there is something of new to read here.

Note: you'll not see this box again if you are a usual reader.

Comments

Nuanda writes:
31 Dec 06, 05:26:14
è curioso, ho letto una cosa molto simile su un altro blog un paio di settimane fa, ma non ricordo ne l'indirizzo ne il contesto...
se lo ritrovo te lo faccio sapere, potrebbe interessarti.
antirez writes:
31 Dec 06, 07:16:31
Ciao Nuanda, l'equivalenza tra il polimorfismo e quella che viene detta programmazione funzionale di ordine piu' alto (usare le funzioni come valori), e' un concetto importante che conosce chiunque si occupa seriamente di linguaggi di programmazione, ma sarei interessato a sapere la fonte se e' in italiano perche' e' da mesi che cerco blog di programmazione in italiano da collegare al mio.

Un'altra equivalenza di questo tipo di cui volevo parlare e' quella tra le chiusure e gli oggetti, in particolare tra chiusure e oggetti in un linguaggio con una OOP prototype-based (come Javascript). Anche di questo vorrei parlare prima o poi.

Colgo lo spunto per dire a tutti quelli che leggono questo blog che se possiedono blog in cui parlarno in italiano di tecnologia possono aspettarsi di essere prontamente linkati dal mio senza alcun tipo di scambio se i loro contenuti sono di qualita'!

Analogalmente se conoscete altri blog che parlano seriamente di tecnologia in italia vi prego di segnalarmeli.
Nuanda writes:
31 Dec 06, 09:02:07
Era in inglese... e mi pare facesse riferimento al linguaggio F#
Fabrizio BU writes:
18 Jan 07, 05:36:21
Ciao Antirex,
non ho potuto leggerti ultimamente e sto recuperando il tempo perso.
devo anche aggiornare il feed rss.

ti ringrazio per questo articolo che mi ha chiarito alcun aspetti sul polimorfismo.
attendo impaziente un esempio di "polimorfismo" o OOP su PHP4 o PHP5

Grazie di tutto!
Fabrizio
comments closed