Gist, il pastebin definitivo

Oggi come oggi esiste una moltitudine di servizi pastebin sparsi per internet ma da qualche giorno è stato aperto quello che attualmente considero essere il pastebin definitivo: Gist. Gist è offerto dall’ormai noto GitHub, il servizio di hosting di progetti probabilmente più in voga in questo momento, e può vantare funzionalità piuttosto interessanti come:

  • supporto per una settantina di linguaggi/grammatiche differenti… anche se manca LOLCode, nonostante ci sia Brainfuck :-)
  • pastie (qui chiamati gist) pubblici o privati, anonimi oppure "identificati" se effettuati tramite il proprio account GitHub. Le operazioni, in questo caso, sono elencate all’interno della dashboard
  • ogni gist può essere composto da file multipli, con possibilità di scaricare comodamente la versione raw di ogni file oppure di effettuare il download del pacchetto intero in formato tar.gz
  • possibilità di modificare i propri gist con tanto di accesso allo storico delle varie revisioni
  • clone URL pubblici e privati per ogni singolo gist, pronti per essere dati in pasto a Git

Provatelo, è veramente ottimo. Giusto per curiosità, questa è la pagina con i miei Gist pubblici…

LuaRocks! Ovvero, un comodo package manager per le librerie di Lua

Lua è un linguaggio di programmazione dinamico e portabile particolarmente noto e utilizzato come linguaggio embdedded grazie ad alcune caratteristiche, proprie e della sua VM, che lo rendono ottimo per lo scripting di applicazioni più o meno complesse scritte in altri linguaggi, solitamente compilati. Ovviamente può essere utilizzato anche per programmare intere applicazioni oppure semplicemente script di automazione, ma in casi simili è molto facile che ci si ritrovi con la necessità di utilizzare librerie esterne da installare e gestire separatamente e manualmente: Lua infatti, diversamente da linguaggi come Ruby o Python, nella sua distribuzione ufficiale non fornisce un set di librerie standard insieme al runtime. Il crescente numero di queste librerie di terze parti ha cominciato a rendere difficile la gestione delle stesse nei propri sistemi e per questo, quasi un anno fa, è nato LuaRocks, un progetto per la realizzazione di sistema di distribuzione e gestione di moduli per Lua che per certi versi risulta essere molto simile a RubyGems.

Attualmente per installare LuaRocks nella maggior parte delle distribuzioni Linux è necessario prelevare il tarball ed eseguire i soliti passaggi ./configure && make && make install, tuttavia l’installazione sulle Ubuntu-derivate a partire da 8.04 Intrepid Ibex o su Debian a partire da 5.0 Lenny sarà molto più semplice dal momento che basterà installare il pacchetto luarocks tramite apt-get. Per Windows esiste un installer che automatizza le procedure, lasciando all’utente la scelta se installare un interprete Lua basilare senza librerie fornito dallo stesso installer o se utilizzare un interprete già installato nel proprio sistema. Tutte le informazioni necessarie al download e all’installazione di LuaRocks sono disponibili sul relativo sito.

Nel frattempo pochi mesi fa è nato un installer di Lua per Windows chiamato, con molta fantasia, Lua for Windows. Esso include molte delle più note librerie per Lua e un ambiente di sviluppo/debug già configurato e basato su SciTE. Si tratta quindi di un’ottima soluzione per avere in pochi secondi tutto il necessario per poter programmare in Lua, tuttavia LuaRocks non è stato ancora incluso per cui ecco i passaggi da seguire per integrare il tutto:

  • Scaricare Lua for Windows (LfW) e procedere con l’installazione: la mia installazione è stata effettuata nel path proposto di default, ovvero C:\Programmi\Lua\5.1
  • Scaricare LuaRocks per Windows, decomprimere l’archivio e aprire la shell dei comandi nella directory contenente install.bat
  • L’installer di LuaRocks permette di specificare alcune opzioni. Ecco come installarlo in modo che sfrutti l’interprete reso disponibile da LfW e che utilizzi un unico path per l’installazione dei pacchetti rock:

    install /LUA C:\Programmi\Lua\5.1 /BIN C:\Programmi\Lua\5.1 /P C:\Programmi\Lua\5.1\LuaRocks\0.6 /CONFIG C:\Programmi\Lua\5.1\LuaRocks /TREE C:\Programmi\Lua\5.1\LuaRocks\ /SCRIPTS C:\Programmi\Lua\5.1\LuaRocks\ /FORCECONFIG

    Di default LuaRocks permette anche ad utenti non amministrativi di installare dei pacchetti rock locali (solitamente in %APPDATA%/luarocks/rocks/), mentre con l’opzione /FORCECONFIG verrà considerato un unico file di configurazione, quello specificato da /CONFIG, e un unico path per l’installazione di rock e script a livello di sistema, specificati rispettivamente da /TREE e /SCRIPT. Potete fare riferimento alla documentazione per un approfondimento in merito.

  • La directory C:\Programmi\Lua\5.1 viene automaticamente aggiunta al PATH di sistema dall’installer di LfW per cui possiamo creare un hardlink ai file luarocks.bat e luarocks-admin.bat con il fine di poter invocare gli stessi più comodamente dalla shell. Da Windows XP in avanti si può operare come segue:

    cd C:\Programmi\Lua\5.1\LuaRocks\0.6
    fsutil hardlink create ../../luarocks.bat luarocks.bat
    fsutil hardlink create ../../luarocks-admin.bat luarocks-admin.bat

  • Modificare la variabile di sistema LUA_PATH aggiungendo ai path già impostati da LfW il seguente percorso per il caricamento dei file della libreria LuaRocks:

    C:\Programmi\Lua\5.1\LuaRocks\0.6\lua\?.lua

Ora LuaRocks è installato e funzionante, basta aprire la shell dei comandi e lanciare luarocks search –all per visualizzare tutti i pacchetti rock disponibili sul server (è disponibile anche un elenco consultabile più comodamente) e luarocks install nomepacchetto per installare i pacchetti rock nel sistema. A questo punto negli script Lua basterà aggiungere la riga require "luarocks.require" che si occuperà di effettuare l’override della funzione require standard di Lua, rendendo possibile il caricamento delle librerie installate tramite LuaRocks. Alternativamente si può ottenere lo stesso risultato lanciando così l’interprete: lua -lluarocks.require

Ecco un esempio di come sfruttare una libreria installata tramite LuaRocks:

-- luarocks install colors require "luarocks.require" require "colors" green = colors.new("#0f8923") print("Verdolino:", green) -- Verdolino: #0f8923

LuaRocks con l’ultima release ha raggiunto un livello di maturità accettabile e l’elenco delle librerie disponibili sotto forma di pacchetti rock è in lento ma costante aumento, ma ci sono alcune applicazioni o framework che ne traggono beneficio per l’installazione e la gestione delle loro dipendenze come per esempio Kepler (un framework per lo sviluppo web generico in Lua), Orbit (un altro framework per lo sviluppo web in Lua, ma MVC oriented) e Sputnik (un wiki scritto in Lua, leggero ma estendibile). Per la cronaca, ecco invece cosa si era costretti a fare solamente un anno fa per avere un’installazione minimale di Lua in Windows e cominciare ad aggiungere librerie come LuaSocket.

We Can’t Stop Here! This Is Bat Country!!

Scusate per il messaggio inutile, ma l’ultimo upgrade di WordPress alla versione 2.6 sembra avermi portato a 2 o 3 problemini sparsi qua e là per cui mi tocca fare qualche prova di troppo. Per renderlo un po’ meno inutile posso dirvi che mi sono finalmente deciso a installare Debian sul mio NSLU2 (per l’occasione ho scelto la più aggiornata Lenny, sono temerario). Il device in oggetto è ufficialmente supportato con alcuni pacchetti mirati specificatamente ad esso ed è un’altra vita rispetto a SlugOS/BE. Nonostante quest’ultima sia leggermente più ottimizzata poiché specificatamente creata per uso su sistemi embedded, con Debian il sistema sembra comportarsi comunque piuttosto bene soprattutto dopo qualche tweak. More to come, ora devo sistemare qualche pezzo di blog…

Ancora su lambda e closure in PHP

Anche se a pochi giorni di distanza, ho pensato di tornare a parlare di funzioni lambda e closure in PHP approfittando di un nuovo aggiornamento della RFC (con relativa patch che implementa quanto descritto) in cui sono state finalizzate alcune idee e sono stati introdotti alcuni nuovi concetti.

Prima di tutto è stata presa una decisione per quanto riguarda la sintassi dichiarativa, infatti precedentemente erano state formulate due possibilità. La prima prevedeva la cattura delle variabili esterne alla chiusura tramite una nuova keyword creata ad hoc, lexical:

$lambda = function($arg1, $arg2) { lexical $reference, &$valueByRef; // ... };

La seconda, quella che è stata effettivamente scelta, prevede invece il riutilizzo della keyword già esistente use:

$lambda = function($arg1, $arg2) use ($reference, &$valueByRef) { // ... };

Qualcuno preferiva la prima opzione e personalmente ero dello stesso avviso, ma alla fine la scelta è stata dettata dall’intenzione di raggiungere un compromesso in maniera tale da ottenere le stesse funzionalità mantenendo la consistenza con l’attuale semantica di PHP e senza intaccare la retrocompatibilità.

La prima estensione nella RFC riguarda invece l’utilizzo della keyword static per stabilire se importare o meno $this all’interno di una chiusura definita all’interno del metodo di una classe. Non effettuare l’importazione di $this all’interno di una chiusura quando non necessario permette di ottenere un risparmio non solo in termini di velocità d’esecuzione, anche si tratta di differenze veramente minimali, ma anche e soprattutto di memoria, evitando che delle long-lived closure possano tenere in vita inutilmente le istanze delle classi in cui esse sono state create. La chiusura che cattura $this infatti impedisce che il refcount interno per l’oggetto che referenzia possa arrivare a 0 e ciò ne comporta l’esclusione dal processo automatico di raccolta delle risorse che viene effettuato dal garbage collector. Vediamo un esempio pratico con tanto di risultato:

class NRK { private $_whoAmI = __CLASS__; public function foo() { $method = __METHOD__; return function() use ($method) { return sprintf("created in %s (w/ \$this):\n%s", $method, print_r($this, true) ?: 'null' ); }; } public function bar() { $method = __METHOD__; return static function() use ($method) { return sprintf("created in %s (w/o \$this):\n%s", $method, print_r($this, true) ?: 'null' ); }; } } $nrk = new NRK(); $fun1 = $nrk->foo(); $fun2 = $nrk->bar(); echo $fun1(), "\n", $fun2(); /* created in NRK::foo (w/ $this): NRK Object ( [_whoAmI:NRK:private] => NRK ) created in NRK::bar (w/o $this): null */

In realtà trovo che sarebbe molto più comodo e pulito avere $this importata in automatico nel caso essa venga esplicitamente richiamata nel corpo della closure, per il compilatore non sarebbe difficile accorgersene ed agire di conseguenza, ad ogni modo non mi piace l’uso della keyword static dal momento che rende ancora più verbosa la definizione di una chiusura.

Proseguendo con le nuove funzionalità, nell’ottica di una maggiore integrazione con PHP sono state estese le classi ReflectionMethod e ReflectionFunction implementando il metodo getClosure() che permette di ottenere una funzione lambda generata dinamicamente partendo da una funzione o da un metodo di istanza/classe:

class NRK { public function instanceMeth() { return __METHOD__; } public static function staticMeth() { return __METHOD__; } public static function methWithArgs($a, $b) { return $a + $b; } } $instance = new NRK(); $class = new ReflectionClass('NRK'); $fun1 = $class->getMethod('instanceMeth')->getClosure($instance); $fun2 = $class->getMethod('staticMeth')->getClosure(); $fun3 = $class->getMethod('methWithArgs')->getClosure(); echo $fun1(), "\n", $fun2(), "\n", $fun3(5, 3); /* NRK::instanceMeth NRK::staticMeth 8 */

Per finire, un’altra novità ispirata dall’implementazione dell’oggetto Closure riguarda la generalizzazione del concetto di oggetto invocabile attraverso l’implementazione di un nuovo metodo magico, __invoke(). Un esempio vale più di mille parole:

class CallableObject { public function __invoke () { return 'Guess what?'; } } $callable = new CallableObject(); $notCallable = new stdClass(); echo '$callable is ', is_callable($callable) ? '' : 'not ', "a callable object\n"; echo '$notCallable is ', is_callable($notCallable) ? '' : 'not ', "a callable object\n"; echo $callable(); echo $notCallable(); /* $callable is a callable object $notCallable is not a callable object Guess what? Fatal error: Function name must be a string in [...] */

Tirando le somme lo stato dell’implementazione di lambda e closure in PHP sembrerebbe essere in uno stato particolarmente avanzato al punto tale che lo stesso Andi Gutmans su php.internals ha chiesto, soprattutto ai release manager, di pensare se sia effettivamente possibile includere tutto questo già in PHP 5.3. In effetti PHP 5.3 sarà già ricco di novità di per sè (namespace e collaterali, LSB, __callStatic, etc) quindi l’introduzione di un’altra nuova feature è da valutare bene, tuttavia vista la buona salute della proposta e dei lavori per implementarla sarebbe un peccato dover aspettare un altro anno o più. Intanto però c’è anche chi si chiede: ma alla fine le novità di PHP 6 saranno rappresentate solo dal supporto per Unicode?

Lambda e closure in PHP 5.3? Forse sì, forse no, ma intanto proviamo.

Negli ultimi giorni sulla mailing list di sviluppo di PHP si sta discutendo sulla possibilità di estendere il linguaggio in maniera tale da introdurre i concetti di funzioni lambda e di closure tanto cari a molti linguaggi dinamici come Ruby, Python, JavaScript, etc. In realtà si potrebbe quasi considerare come un ritorno di fiamma, già in passato ci furono discussioni simili anche se poco convinte e poco convincenti, tuttavia questa volta si intravede la possibilità di sfociare in una decisione furba nonostante non ci sia particolare interesse nell’implementazione da parte di alcuni dei core-dev (tanto per cambiare). Attualmente il tutto ruota intorno a una RFC che, oltre a mettere in evidenza alcuni scenari sull’utilizzo di funzioni lambda e chiusure e riportare i dettagli implementativi nell’engine di Zend, allega alcune patch con alcune piccole varianti sul tema da applicare ai sorgenti nei rami di sviluppo di PHP 5.3 e PHP 6.0 per poter toccare con mano quanto si sta discutendo.

$fun = function($name = 'guy') { echo "Hi $name!"; }; printf("Tipo: %s\n", get_class($fun)); // Type: Closure printf("Callable: %s\n", is_callable($fun)); // Callable: 1

Come possiamo notare, è stato implementato un nuovo tipo in ZE denominato Closure. Closure risulta essere invocabile, alla stessa stregua di funzioni e metodi:

echo $fun(); // Hi guy! echo $fun('NRK'); // Hi NRK!

Il primo pensiero va a questo punto a tutte quelle funzioni in PHP che accettano delle callback ma in cui comodità ed espressività vengono pesantemente minate dall’attuale modus operandi del linguaggio che prevede due opzioni:

  • definire una funzione tradizionale da usare come argomento callback ma sotto forma di stringa (il nome della funzione stessa), dando quindi origine a potenziali bugs galore.
  • creare una funzione a runtime usando create_function, opzione che oltre a risultare poco elegante alla vista rischia di introdurre un’altra buona dose di problemi in termini di manutenibilità del codice.

Ecco quindi un confronto tra come è possibile scrivere codice oggi e la possibilità di sviluppare la stessa soluzione attraverso l’utilizzo delle funzioni lambda:

$array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); /* senza funzioni lambda */ function reducer($a, $b) { return $a * $b; } $res = array_reduce($array, 'reducer', 1); // 3628800 /* senza funzioni lambda */ $res = array_reduce($array, create_function('$a, $b', 'return $a * $b;'), 1); // 3628800 /* con funzion lambda - versione "lunga" */ $reducer = function($a, $b) { return $a * $b; }; $res = array_reduce($array, $reducer, 1); // 3628800 /* con funzion lambda - versione "breve" */ $res = array_reduce($array, function($a, $b) { return $a * $b; }, 1); // 3628800

Il fatto che l’oggetto Closure sia a tutti gli effetti visibile in userland permette anche di sfruttare l’hinting dei tipi nel caso volessimo limitare a questo tipo di oggetto alcuni parametri delle nostre funzioni:

function iterate($array, Closure $fun) { foreach ($array as $element) $fun($element); } iterate($array, 'function'); // genera un Catchable Fatal Error, Argument 2 passed to // iterate() must be an instance of Closure, string given

Essendo Closure una vera e propria classe (che, per la cronaca, attualmente espone solo un metodo __toString() seppur vuoto) potrebbe implementare in un ipotetico futuro diverse funzionalità interessanti e chissà, potrebbe anche essere serializzabile. Ad ogni modo, dopo le funzioni lambda passiamo a vedere le closure:

$array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $sum = 0; $fun = function($element) { lexical &$sum; // ... $sum += $element; }; array_walk($array, $fun); echo $sum; // 55

Possiamo notare l’introduzione della keyword lexical (c’è anche la variante use a seconda della patch che si decide di utilizzare) che ci permette di catturare i riferimenti alle variabili esterne al corpo della chiusura. L’introduzione di lexical è dovuta al fatto che con questa implementazione non viene catturato tutto l’ambiente lessicale in cui viene creata la chiusura, come accade invece in altri linguaggi, per motivi legati principalmente alle regole di scope di PHP. Usando lexical possiamo pertanto decidere quali variabili catturare, ma è da notare che attualmente per i tipi numerici o stringa, che per default in PHP sono passati per valore, è necessario creare manualmente un riferimento anteponendo l’ampersand alla variabile catturata, come da esempio. Tecnicamente la cattura per riferimento dovrebbe essere di default anche per questi tipi e lo stesso creatore della patch è del medesimo avviso, tanto più che il terribile global si comporta nello stesso modo creando sempre riferimenti.

In linea di massima l’introduzione di funzioni lambda e closure in PHP sarebbe interessante e in certe situazioni renderebbe la scrittura dei programmi un po’ meno arrabattata con la possibilità di ottenere codice molto più facilmente generalizzabile e più verificabile/manutenibile. Provare è abbastanza facile: basta avere un ambiente configurato per compilare PHP, scaricare i sorgenti di PHP 5.3 o 6.0 dal repository CVS (sì, usano ancora CVS…), applicare una delle patch disponibili, compilare e installare.