Falcon (linguaggio di programmazione)

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca
Falcon
linguaggio di programmazione
AutoreGiancarlo Niccolai
Data di origine2003
Ultima versione0.9.6.8
Utilizzolinguaggio di scripting
Paradigmimulti-paradigma
Tipizzazionedebole
Influenzato daPerl, Smalltalk e PHP
Implementazione di riferimento
Sistema operativomulti-piattaforma
Licenzasoftware libero
Sito webwww.falconpl.org

Il linguaggio di programmazione Falcon, abbreviabile come Falcon P.L., Falconpl o Falcon, è un linguaggio di scripting multi paradigma open source, sviluppato da Giancarlo Niccolai, inizialmente nell'ambito della propria attività lavorativa, e pubblicato successivamente come progetto Open Source[1].

Storia[modifica | modifica wikitesto]

Le origini risalgono a un progetto denominato HASTE, avviato nel 2002 (Haste Advanced Simple Text Evaluator). Si trattava di un motore di scripting pensato principalmente per l'inclusione in applicazioni dinamiche e che mirava a realizzare una macchina virtuale veloce e leggera, con capacità di generare un alto throughput di dati a basse latenze in ambienti fortemente multithreading, per la configurazione dinamica di applicazioni in ambito finanziario[2]. Successivi adattamenti e implementazioni, basate principalmente sull'esperienza e sulle necessità operative, e sui suggerimenti dei primi utenti hanno dato origine al Falcon attuale. La prima versione distribuita attraverso pacchetti ufficiali su distribuzioni di software Open Source è stata la 0.8.8, nel maggio del 2008.

Filosofia[modifica | modifica wikitesto]

Falcon fonde diversi stili di programmazione (detti paradigmi) in un unico ambiente.

Da un punto di vista implementativo Falcon si propone come linguaggio stand-alone ma con una forte predisposizione verso la cooperazione con applicativi preesistenti, meglio ancora in ambito multi-threading.

Hello World[modifica | modifica wikitesto]

Il seguente esempio propone il classico "Hello, World!" utilizzando l'operatore di stampa veloce ">", o, in forma più classica, attraverso la funzione printl:

> "Hello World!"
printl("Hello World again!")

Falcon supporta tutti caratteri Unicode negli script sorgenti:

// International class; name and street
class 国際(なまえ, Straße)
   // set class name and street address
   नाम = なまえ
   شَارِع   =  Straße
   // Say who am I!
   function 言え!()
     >@"I am $(self.नाम) from ",self.شَارِع
   end
end
// all the people of the world!
民族 = [ 国際( "高田 Friederich", "台湾" ),
   国際( "Smith Σωκράτης", "Cantù" ),
   国際( "Stanisław Lec", "południow" ) ]
for garçon in 民族: garçon.言え!()

Tipi di dati[modifica | modifica wikitesto]

  • Nil - Il classico "non valore".
  • Integer - intero a 64 bit.
  • Numeric - Numero in virgola mobile a 64 bit compatibile con le specifiche IEEE.
  • Range - una tripletta costituita da un limite inferiore, uno superiore e un passo.
  • MemBuf - Buffer di memoria dove ogni elemento è un intero senza segno costituito da 1, 2, 3 o 4 bytes.
  • Function - Le classiche funzioni, ovvero porzioni di codice richiamabili in altre parti del programma.
  • String - Sequenza mutabile di caratteri UNICODE.
  • Array - Sequenza mutabile di istanze di varia natura.
  • Dictionary - Sequenze mutabili costituite da coppie chiave-valore.
  • Oggetti - Istanze derivate da classi o oggetti privi di classe di derivazione.
  • Class - Classi che possono generare istanze (oggetti).
  • Method - Sono le funzioni da applicare su una qualsiasi istanza.

Paradigmi supportati[modifica | modifica wikitesto]

Falcon supporta al momento 6 paradigmi di programmazione:

  • procedurale
  • funzionale
  • orientato agli oggetti (OOP)
  • OOP basata sui prototipi
  • orientata ai messaggi
  • programmazione tabellare

Procedurale[modifica | modifica wikitesto]

La programmazione procedurale si base sulle classiche dichiarazioni e chiamate di funzioni. Tutte le funzioni supportano implicitamente un numero variabile di parametri, oltre a parametri posizionali/nominali.

Il seguente è un programma completo, interamente procedurale, che illustra alcune decisioni implementative che riguardano la struttura dei loop e il processamento imperativo delle sequenze:

function sayList(saying)
   for elem in saying
       >> elem
       formiddle: >> " "
       forlast: > "!"
   end
end
sayList(List("Have", "a", "nice", "day") )

Funzionale[modifica | modifica wikitesto]

Il motore di valutazione chiamato Sigma-riduttore interno permette di scrivere programmi in puro stile funzionale, non diversamente da quanto possibile fare in linguaggi come il Lisp. Comunque resta possibile miscelare diversi stili di programmazione (come approcci OOP o puramente imperativi) direttamente nelle sequenze funzionali.

Le sequenze funzionali sono costituite da normali array; questo significa che possono essere create, controllate e modificate dinamicamente dal programma stesso, sia fra valutazioni distinte che durante il corso di una valutazione di Sigma-riduzione. Il seguente esempio illustra la possibilità di cambiare dall'interno della sequenza un particolare tipo di referenza a variabile, chiamato late binding.

 pnext = [ printl, '"', &value, '"' ]
 dolist(function(p); seq.value = p; eval(seq); end,
         ["Have", "a", "nice", "day"] )

È anche possibile chiamare direttamente le sequenze funzionali se il loro primo membro è esso stesso un valore chiamabile, come nel seguente esempio:

[printl "Prompt> "]( "Real data to print" )

Sequenze funzionali con un solo livello di profondità (come quelle viste in questi esempi) possono essere viste come chiamate pre-registrate (cached calls), e una volta assegnate a una variabile sono morfologicamente equivalenti ai simboli che identificano le funzioni stesse.

Il paradigma funzionale è completato dal marcatore di valore fuori banda (out of band item marker). Ogni valore può ricevere il marcatore oob, la cui presenza può essere verificata in un secondo momento attraverso operatori di linguaggio e/o funzioni di libreria. Si tratta di un segnale che indica una significanza speciale del valore marcato e che può alterare il comportamento delle sequenze funzionali attraverso le quali viene fatto viaggiare. Per esempio molti cicli funzionali, come floop o times, possono essere fatti ripartire o interrotti ritornando rispettivamente un valore oob 1 o 0 da una delle funzioni della sequenza. Oppure, la funzione map, che trasforma tutti i valori contenuti in una sequenza applicandovi una funzione di trasformazione, ignorerà (scartandolo) un valore nil marcato oob; in questo modo è possibile effettuare operazioni di mappatura e filtro in un singolo passaggio.

Orientato agli oggetti (OOP)[modifica | modifica wikitesto]

Falcon adotta un modello di Programmazione orientata agli oggetti che include classi, ereditarietà, membri statici, inizializzatori di proprietà e costruttori. L'ereditarietà multipla è supportata a condizione che al massimo una delle classi nella gerarchia sia riflessiva (cioè, rifletta dati direttamente forniti dalle applicazioni o dai moduli di terze parti in modalità nativa). È supportato anche l'accesso ai membri delle classi base.

La struttura delle istanze è fissa e immutabile, ma data la natura funzionale di Falcon, che vede le funzioni come un tipo particolare di dati, è possibile impostare i membri di un'istanza come semplici dati o come funzioni (rendendoli quindi metodi) dinamicamente. Sequenze funzionali possono essere assegnate alle proprietà, nel cui casi diventano metodi funzionali per la data istanza.

Falcon supporta oggetti primitivi, ossia oggetti non dotati di classe specifica, che possono essere sia totalmente privi di classe che parzialmente derivati da una o più classi parente. Gli oggetti primitivi vengono istanziati e impostati prima che la Macchina Virtuale esegua il programma principale. L'ordine di risoluzione delle istanze è gestito dal Linker di Falcon, che assicura una corretta inizializzazione pur in caso di referenze incrociate fra oggetti primitivi nello stesso modulo.

Le classi possono essere istanziate anche attraverso sequenze funzionali: l'istantazione di una classe (la creazione di un'istanza) è morfologicamente equivalente a "chiamare" il simbolo della data classe. Quindi, valutare una sequenza funzionale che comincia con il simbolo di una classe equivale a chiederne l'istantazione.

Il modello OOP di Falcon è completato dal sovraccarico degli operatori, che permette di creare classi sulle quali gli operatori di linguaggio logici e matematici si comportano in modo specializzato.

OOP basata sui prototipi[modifica | modifica wikitesto]

La programmazione orientata ai prototipi è simile al paradigma OOP, ma è privo del concetto di classe. Le istanze sono tutte prive di classe e la loro struttura può essere cambiata dinamicamente. I dizionari del linguaggio Falcon (tipo primitivo costituito da collezioni di coppie chiave univoca/valore) possono includere funzioni; possono quindi essere blessed, informando così il sistema che sono da trattare come istanze prive di classe. Applicando l'accessore punto è possibile accedere ai loro membri come metodi o proprietà. Nel seguente esempio un dizionario diventa oggetto:

dict = bless([ 'state' => 0, 'incme' => function(); self.state++; end ])
dict.incme()
> dict.state  // stampa '1'

Un meccanismo simile può essere applicato agli array tramite i binding:

array = [1,2,3]
array.showMe = function()
   for item in self: > item
end
array.showMe()

Orientata ai messaggi[modifica | modifica wikitesto]

La programmazione orientata ai messaggi è costituita dall'invocazione di uno o più gestori alla trasmissione di un messaggio. Il contenuto del messaggio è arbitrario, e può essere costituito da qualsiasi tipo di dato, incluse classi (dalle quali creare istanze al volo), sequenze funzionali o tabelle. I ricevitori possono competere per ricevere il messaggio escludendo gli altri gestori, o possono partecipare nella costruzione di una risposta comune in passaggi ordinati. I messaggi possono richiedere una gestione immediata all'atto della trasmissione, oppure essere lasciati nell'ambiente per essere ricevuti e gestiti da sottoscrittori tardivi (così dette "asserzioni").

La programmazione orientata ai messaggi si interfaccia direttamente con la Macchina Virtuale, con la quale moduli nativi e applicazioni parente possono interagire. Per esempio un'applicazione multithreaded può lanciare nella macchina virtuale messaggi che giungono da thread differenti per una gestione serializzata a livello di script, e una susseguente ri-trasmissione asincrona dei messaggi elaborati direttamente dall'interno degli script.

Programmazione tabellare[modifica | modifica wikitesto]

La programmazione tabellare è una speciale estensione e generalizzazione dell'OOP, dove una classe è rappresentata da una tabella, le cui colonne sono proprietà e ogni riga è un'istanza. Oltre a mantenere tutte le istanze ordinatamente assieme, e permettere a ogni istanza di lavorare con quelle adiacenti accedendo alla tabella madre, modifiche sulla struttura della tabella sono immediatamente riflesse su tutte le istanze.

Le tabelle offrono un supporto diretto alla selezione di un comportamento o un insieme di comportamenti da una serie finita di scelte possibili, fornendo la base per sviluppare direttamente nel linguaggio motori decisionali a logica discreta o fuzzy. Ogni riga, fisicamente costituita da un dato di tipo Array, può contenere sia dati e logiche specifiche della tabella che dati o logiche private dell'istanza (grazie ai Binding), quindi un'entità selezionata da una logica di estrazione globale può fornire comportamenti altamente specializzati.

Altre caratteristiche[modifica | modifica wikitesto]

Oltre a fornire diversi stili di programmazione Falcon presenta caratteristiche trasversali a diversi modelli; di seguito sono indicate le più rilevanti.

Descrizioni di liste[modifica | modifica wikitesto]

Tipi di base e classi che espongono un'interfaccia di sequenza verso il motore interno offrono un metodo "comp", che permette di sintetizzare insiemi a partire da una definizione matematica.

Il metodo "comp" richiede l'indicazione obbligatoria di un parametro "origine", che può essere un'altra sequenza, un valore di tipo "range" o una funzione generatrice che torna un valore per volta, e un marcatore speciale per dichiarare la fine della sequenza.

Una funzione (o in generale un'entità chiamabile) può essere fornito come parametro opzionale e agisce sia come filtro che come modificatore.

Sequenze associative (per esempio dizionari) sono supportati.

Il seguente è un semplice esempio che usa un range per creare un array contenente i numeri pari tra 2 e 10, attraverso comprensione di lista:

 even_array = [].comp( [2:11:2] )

Per generare una lista di numeri interi casuali fra 1 e 9, di lunghezza anch'essa casuale:

random_list = List().comp(function(); n = random(1,10); return n == 10? oob(0): n; end)

La funzione generatrice, fornita in linea, torna il marcatore di fine sequenza "oob(0)" quando viene generato casualmente il numero 10 (quindi a ogni nuova iterazione c'è 1/10 di probabilità di terminare la lista).

Il prossimo esempio, più completo, usa un generatore per riempire un insieme di tipo "Set" con esattamente dieci numeri interi casuali nell'intervallo 1..100. In questa occasione è il filtro stesso che determina quando la comprensione è completa.

random_set = Set().comp(
    [random, 1, 100],  // generator, callable array
    function(number, myself)  // filter, using the optional "myself" param
       if myself.len() == 10
           return oob(0)  // return oob(0) as a generator to terminate
       end
       return number
    end
    )

Il metodo "comp" torna lo stesso oggetto su cui è applicato, e può essere applicato anche su sequenze non vuote.

Similmente, il metodo "mfcomp" permette di creare descrizioni di liste a partire da insiemi multipli, come nel seguente esempio:

 sums = [].mfcomp( {x,y=> x+y}, .[1 2 3], .[4 5 6] )

Documenti Maschera[modifica | modifica wikitesto]

Falcon supporta script inseriti in documenti di testo tramite le direttive di preprocessore <? .. ?> o <?fal... ?>. Gli script salvati con estensione ".ftd" sono trattati come semplici documenti di testo e semplicemente trascritti sull'output fino a che una delle suddette direttive di preprocessore vengono incontrate. Script inclusi nelle direttive sono eseguiti in linea, come nel seguente esempio:

 Hai chiamato questo programma passando <? print(args.len() ) ?> parametri.

I documenti FTD (Falcon template documents) possono fare parte di applicazioni più ampie, costituite in parte da normali moduli e in parte da documenti maschera dinamici, così che è possibile delegare la logica di presentazione alle maschere dinamiche e mantenere la logica applicativa in moduli Falcon.

I documenti FTD possono essere utilizzati nella realizzazione di siti web dinamici. Per alcuni web server (al momento Apache 2) sono disponibili dei moduli integrati che possono eseguire direttamente documenti FTD e moduli Falcon, esponendo verso di questi l'API del web server ospite. È anche possibile usare pagine FTD dinamiche in modalità CGI.

Eccezioni[modifica | modifica wikitesto]

Il meccanismo delle eccezioni si basa sulle istruzioni "raise", "try" e "catch". L'istruzione raise può sollevare qualsiasi tipo di dato, inclusi nil, numeri, stringhe, oggetti ecc. Funzioni di libreria e moduli esterni sollevano generalmente istanze della classe Error, o istanze di classi derivate da essa.

L'istruzione catch può essere utilizzata per catturare un'eccezione di qualsiasi tipo, o può filtrare le istanze di certe classi, o filtrare solo alcuni tipi di dato. La cattura delle istanze di classe è organizzata su base gerarchica; è quindi possibile fornire gestori di eccezioni più specifici ed eventualmente gestori generici di default, come nel seguente esempio (la classe TypeError è derivata da Error):

try
   ... codice che può generare un errore...
catch TypeError in error
   ... abbiamo commesso un errore di tipo...
catch Error in error
   ... abbiamo commesso un altro errore generico...
catch StringType in error
   ... è stata sollevata esplicitamente una stringa...
catch in error
   ... è stato sollevato qualcosa di diverso...
end

La clausola in dell'istruzione catch è opzionale (questo significa che il contenuto dell'errore può essere scartato).

L'istruzione catch si comporta in modo simile all'istruzione select, che può essere usata per eseguire codice in base al tipo o alla classe della variabile su cui è applicato.

Runtime integrabile[modifica | modifica wikitesto]

Falcon, tramite la libreria specifica libfalcon, è in grado di interfacciarsi con altri sistemi, eventualmente estendendoli.

Generazione di documentazione[modifica | modifica wikitesto]

Falcon dispone di uno strumento di documentazione integrato chiamato Faldoc, studiato per estrarre documentazione in-linea sia da moduli scritti in Falcon che da moduli nativi in C/C++.

Filesystem virtuale[modifica | modifica wikitesto]

Tutte le operazioni di I/O che avvengono sia al livello di motore interno, sia a livello di macchina virtuale (esecuzione dei programmi) sono delegate a un Virtual Filesystem Provider centralizzato. I Filesystem virtuali sottoscritti al provider astraggono operazioni I/O come la lettura delle directory, la creazione dei file, l'apertura di stream ecc., e possono essere invocati attraverso il loro indirizzo URI. Questo rende possibile il caricamento di moduli o l'apertura di risorse da qualsiasi Filesystem virtuale, (come risorse di rete o archivi compressi/crittografati), che possono fornire locazioni speciali riconosciute da moduli di terze parti o da applicazioni che integrano il motore Falcon.

Supporto alla concorrenza[modifica | modifica wikitesto]

A partire dalla versione 0.8.x è stato introdotto un pieno supporto alla concorrenza secondo una modalità agent-oriented. È possibile scambiare dati attraverso vari meccanismi di condivisione mentre ogni singolo thread gira in una propria macchina virtuale che non viene in alcun modo interessata da quanto avviene nelle altre. Questo sistema implica un pieno parallelismo e un elevato grado di sicurezza in fase di esecuzione.

Coroutine[modifica | modifica wikitesto]

Falcon supporta un sistema di concorrenza quasi-parallelo (detto coroutining). Le coroutine sono costituite da codice eseguito in frazioni di tempo differenti, o durante i tempi di pausa della Macchina Virtuale. Questo meccanismo fornisce un parallelismo software più leggero del multithreading a livello hardware, e permette la piena visibilità dei dati globali in ogni agente del processo parallelo, ma richiede l'esplicita collaborazione di ogni coroutine al processo (per esempio evitando di effettuare chiamate bloccanti per l'intera macchina virtuale) e non permette di sfruttare hardware paralleli.

Metacompilatore[modifica | modifica wikitesto]

Il compilatore Falcon ha un meta-compilatore che supporta l'espansione di macro. Il meta-compilatore è pilotato da una Macchina Virtuale Falcon applicata al compilatore di base; l'output generato dal meta-compilatore è sovrapposto allo stream di ingresso del compilatore come se si trattasse del sorgente originale. Usando la sequenza di escape \[...\] è possibile creare i contenuti del programma di base dinamicamente semplicemente usando le routine di output. Per esempio:

\[ printl( "printl( 'Ciao mondo!' )" ) \]

La parola chiave macro fornisce una grammatica semplificata per un accesso più semplice alla meta-programmazione effettuata al volo durante la compilazione.

Internazionalizzazione nativa[modifica | modifica wikitesto]

Le stringhe con il prefisso 'i' sono riconosciute come stringhe internazionali per l'esportazione. È anche possibile dichiarare la lingua nel quale i messaggi internazionalizzati sono scritti tramite l'istruzione directive lang, come nel seguente esempio:

 directive lang=fr_FR           // uses 5 characters ISO language code
 > i"Bonjour à tout le monde!"

Un tool da riga di comando chiamato fallc consente di esportare le stringhe 'i' in un file di definizione XML, che può essere usato come maschera per la traduzione delle stringhe in altre lingue.

La tavola delle stringhe della lingua in cui l'applicazione deve essere tradotta è applicata dinamicamente al momento del caricamento del modulo (prima dell'esecuzione).

Feathers[modifica | modifica wikitesto]

Falcon supporta la programmazione modulare e dispone di una suite di moduli standard chiamata Feathers che viene distribuita con l'installazione di default. Essa comprende i seguenti moduli:

  1. Compiler - permette di caricare dinamicamente altri moduli o precompilati Falcon
  2. Configuration Parser - interfaccia semplice ma potente per la configurazione
  3. funcext - contiene una serie di estensioni utili in ambito funzionale
  4. logging - gestisce log su file e su strumenti di log di sistema (Event Logger, Syslogd, ecc.)
  5. JSON - Routine di conversione di oggetti Falcon da e verso rappresentazione JSON.
  6. MXML - Parser e generatore XML
  7. Regular Expression - come dice il nome modulo per la gestione delle espressioni regolari
  8. Socket - per la gestione delle funzionalità TCP.
  9. Threading - Costrutti per il multithreading nativo.
  10. Zlib - interfaccia verso le routine di compressione ZLib.

Implementazione[modifica | modifica wikitesto]

Falcon in tutte le sue parti critiche, dalla VM ai moduli ufficiali, è completamente scritto in C++, tranne alcune parti a basso livello che fanno uso di C e Assembly.

Disponibilità[modifica | modifica wikitesto]

Falcon è distribuito tramite installatori specifici su sistemi Mac OS X Leopard e MS-Windows (sui quali la compilazione e l'installazione sono processi più articolati), o tramite pacchetti sorgente auto-generanti su sistemi aperti come Linux o OpenSolaris.

Su questi sistemi il Falcon Programming Language è generalmente supportato e aggiornato da varie distribuzioni, fra cui:

Il Falcon Programming Language è disponibile sui sistemi Solaris attraverso il progetto Blastwave, e tramite la distribuzione di OpenSolaris detta AuroraUX.

Note[modifica | modifica wikitesto]

Bibliografia[modifica | modifica wikitesto]

Collegamenti esterni[modifica | modifica wikitesto]

  Portale Informatica: accedi alle voci di Wikipedia che trattano di Informatica