Visualizzazione post con etichetta ruby. Mostra tutti i post
Visualizzazione post con etichetta ruby. Mostra tutti i post

martedì 22 gennaio 2008

Mixin come interfacce in Ruby

Un utilizzo forse un po' anomalo dei mixin in Ruby è quello di considerarli alla stregua di interfacce per implementare un comportamento polimorfico.

Chi conosce almeno un linguaggio a oggetti puro (come Java o C#) sa che le interfacce sono dei contratti che il programmatore stipula con l'utilizzatore del suo codice sorgente per garantirgli che una classe "implementa" certi metodi, con certi parametri. Le interfacce sono alla base dell'utilizzo del polimorfismo nei linguaggi a oggetti compilati.

In genere nei linguaggi di scripting a oggetti, possiamo utilizzare il polimorfismo anche senza le interfacce, cioè ammettendo che, se l'oggetto passato al metodo polimorfico non implementa determinati metodi, avremo un errore a run-time.

In realtà a volte può essere difficile se non impossibile, non avendo a disposizione ad esempio il commento di un certo codice sorgente, sapere se una certa classe è stata progettata per un utilizzo polimorfico.

Bene, grazie ai mixin, noi possiamo rendere esplicita la nostra intenzione di voler far implementare a una classe una determinata interfaccia, o, in altri termini, ad assicurare il programmatore che utilizzerà il nostro codice, che questo utilizza un certo modulo come interfaccia, quindi garantendo che i metodi in quel modulo saranno implementati.

Ad esempio:


module Animale
def mangio
puts "Sono un animale e mangio"
end
end

class Uccello
def volo
puts "Sono un uccello e volo"
end
end

class Gazzella < Uccello
include Animale

def mangio
puts "Sono una gazzella e mangio"
end
end

def metodopolimorfico(animale)
animale.mangio
end

metodopolimorfico(Gazzella.new)

In questo codice noi abbiamo una classe di Gazzelle che eredita dalla classe base Uccello. Qui il programmatore vuole dirci che Gazzella è (IS-A) sì un Uccello, ma è "anche" un Animale (infatti implementa "mangio"). In questo modo il client-programmer che vuole sapere se potrà passare una "Gazzella" al suo metodo "metodopolimorfico" che prende in ingresso un "Animale" è assicurato: siccome Gazzella è un mixin di Animale, sicuramente rispetta l'interfaccia di Animale (che nel nostro caso è il metodo mangio).

Non solo: Gazzella può fare l'override di un metodo incluso. Infatti lo fa, e "mangio" di Gazzella ha un'implementazione diversa da "mangio" di Animale. Chiaramente, poteva anche non farlo, mantenendo l'implementazione originale (e qui sta la differenza con le interfacce, che sono sempre astratte e vanno sempre e comunque implementate).


martedì 20 marzo 2007

Espressività e sinteticità di un linguaggio

La recente discussione su Artima che riguarda l'espressività e la sinteticità di un linguaggio (http://www.artima.com/forums/flat.jsp?forum=276&thread=198171) mi spinge a fare qualche altra riflessione, visto che sull'argomento avevo già speso in passato qualche parola.
In questo forum si discute della sinteticità e dell'espressività di un linguaggio come sinonimi (e si sottolinea che non lo sono, o meglio, non è sempre detto che lo siano).
Un linguaggio per computer è conciso quando permette di fare "tanto" con "poche" righe di codice. Prendete ad esempio questa classica ricetta in Python:

thelist.sort()
item_insert_point = bisect.bisect(thelist, theitem)
is_present = thelist[item_insert_point-1 : item_insert_point] == [theitem]


Questo codice permette di cercare un item all'interno di una lista ordinata usando un algoritmo di ricerca binario. Sfido chiunque a fare la stessa cosa, per esempio, in Java, in 3 linee di codice. Dunque, Python è un linguaggio sintetico. (Il che significa spesso che ha delle API molto avanzate: in questo caso tutto il lavoro lo fa bisect).
Eppure, il codice sopra è poco espressivo. Ho detto che per espressività in genere si intende la capacità di un linguaggio di essere:

  • leggibile (cioé facilmente comprensibile a chi abbia una ragionevole dimestichezza con i linguaggi per computer)
  • potente (cioé possa esprimere un algoritmo in molti modi diversi)

Sicuramente è poco leggibile - tranne che dai fan di Python! Però capiamoci, non è vero che Python (ma potrei dire Ruby o Groovy o qualsiasi altro linguaggio della scorsa generazione, cioé prima di Scala, per intenderci) sia poco espressivo. Al contrario, viene sempre indicato come un linguaggio super-espressivo (e ciascun programmatore in Python vi dirà che è contento di Python proprio perché permette di fare "tutto" con pochissimo sforzo).


Allora qual è il nocciolo della questione? E' che spesso si confonde l'espressività di un linguaggio con la sua capacità di offrire "zucchero sintattico" per fare le cose normali, senza però aggiungere veramente qualcosa di nuovo. La sinteticità, in fondo, è sempre "zucchero sintattico", a volte è solo un virtuosismo inutile. Perciò, l'espressività di un linguaggio non la misurerei in varianti sintattiche per fare la stessa cosa (esempio, contare da 1 a 1000), ma nella sua capacità di fare cose che altri linguaggi non sono in grado di fare (esempio, le closure di cui parlavamo). Con nuova espressività, un linguaggio è in grado di annoverare tra le sue API, delle API che in altri linguaggi non sono nemmeno immaginabili.


Perché queste cose noiose sono così interessanti? Perché in realtà qui si gioca la nostra capacità di utilizzare i computer per fargli fare - senza perderci una vita - ciò che vogliamo: nuovi modi per lavorare, la possibilità di automatizzare alcuni compiti, o anche nuovi modi per divertirci, dalla comunicazione ai videogiochi. Linguaggi per computer più espressivi ci permetteranno di fare con loro - insieme all'evoluzione tecnologica dell'hardware - cose prima inimmaginabili.