2009-04-29 5 views
6

Quindi attualmente sto lavorando su un nuovo linguaggio di programmazione. Ispirato da idee provenienti dalla programmazione concorrente e da Haskell, uno degli obiettivi principali della lingua è la gestione degli effetti collaterali. Più o meno, ad ogni modulo sarà richiesto di specificare quali effetti collaterali consenta. Quindi, se stessi facendo un gioco, il modulo grafico non avrebbe alcuna capacità di fare IO. Il modulo di input non avrebbe la capacità di disegnare sullo schermo. Il modulo AI dovrebbe essere totalmente puro. Script e plugin per il gioco avrebbero accesso a un sottoinsieme molto ristretto di IO per leggere i file di configurazione. Et cetera.Come devo gestire gli effetti collaterali in un nuovo design della lingua?

Tuttavia, ciò che costituisce un effetto collaterale non è chiaro. Sto cercando qualsiasi pensiero o suggerimento sull'argomento che potrei prendere in considerazione nella mia lingua. Ecco i miei pensieri correnti.

Alcuni effetti collaterali sono evidenti. Che si tratti di stampare sulla console dell'utente o di lanciare i missili, qualsiasi azione che legga o scriva in un file di proprietà dell'utente o che interagisca con l'hardware esterno è un effetto collaterale.

Gli altri sono più sottili e questi sono quelli a cui sono veramente interessato. Sarebbero cose come ottenere un numero casuale, ottenere il tempo di sistema, dormire un thread, implementare la memoria transazionale del software o anche qualcosa di molto fondamentale come allocare memoria.

A differenza di altri linguaggi creati per controllare gli effetti collaterali (guardando Haskell), voglio progettare il mio linguaggio in modo pragmatico e pratico. Le restrizioni sugli effetti collaterali dovrebbero servire a due scopi:

  • Per aiutare nelle separazioni di preoccupazioni. (Nessun modulo può fare tutto).
  • Per eseguire la sandbox di ciascun modulo nell'applicazione. (Qualsiasi modulo può essere utilizzato come plug-in)

Con questo in mente, come dovrei gestire gli effetti "pseudo" sul lato, come numeri casuali e dormendo, come ho detto sopra? Cos'altro potrei aver perso? In che modo posso gestire l'utilizzo della memoria e il tempo come risorse?

+0

+1 Wow. In realtà stavo solo facendo una domanda simile. Sto progettando un linguaggio ibrido tipo FP/OOP. – Zifre

risposta

0

Dai uno sguardo serio a Clojure e il loro uso di software transactional memory, agents, and atoms per mantenere gli effetti collaterali sotto controllo.

+0

Devo ammettere che ho setacciato il sito Clojure il giorno in cui l'hanno annunciato per la prima volta su Reddit. Penso che la lingua sia davvero fantastica. Volevo entrare nel loro sviluppo, ma c'è qualcosa in cui le lingue libere sono assolutamente poco attraenti per me. Sarò sicuro di controllare quali progressi hanno fatto da allora per le idee! –

+0

Sono curioso di sapere cosa non è pratico e pratico su Haskell. È * diverso *, certo, bt c'è un bel po 'di vero software scritto in esso. Si potrebbe anche guardare Erlang, che sta sicuramente vedendo un uso pratico. –

+0

Oppure, in inglese, "quello che senti non è pragmatico e pratico". –

1

Un effetto collaterale sta avendo alcun effetto su qualsiasi cosa nel mondo oltre a restituire un valore, cioè a mutare qualcosa che potrebbe essere visibile in qualche modo al di fuori della funzione.

Una funzione pura non dipende né influisce su uno stato mutabile al di fuori dello scopo di tale invocazione della funzione, il che significa che l'uscita della funzione dipende solo da costanti e dai relativi input. Ciò implica che se si richiama una funzione due volte con gli stessi argomenti, si è certi di ottenere lo stesso risultato entrambe le volte, indipendentemente dalla modalità di scrittura della funzione.

Se si dispone di una funzione che modifica una variabile che è stata passata, tale modifica è un effetto collaterale poiché è l'output visibile dalla funzione diversa dal valore restituito. Una funzione di vuoto che non è un no-op deve avere effetti collaterali, perché non ha altro modo di influenzare il mondo.

La funzione potrebbe avere una variabile privata visibile solo a quella funzione che legge e modifica e chiamarla avrebbe comunque l'effetto collaterale di cambiare il modo in cui la funzione si comporta in futuro. Essere puri significa avere esattamente un canale per l'output di qualsiasi tipo: il valore di ritorno.

È possibile generare numeri casuali puramente, ma è necessario passare manualmente il seme casuale.La maggior parte delle funzioni casuali mantiene un valore di seed privato che viene aggiornato ogni volta che viene richiamato in modo da ottenere un random diverso ogni volta. Ecco un Haskell snippet utilizzando System.Random:

randomColor    :: StdGen -> (Color, Int, StdGen) 
randomColor gen1   = (color, intensity, gen2) 
where (color, gen2)  = random gen1 
     (intensity, gen3) = randomR (1, 100) gen2 

Le funzioni casuali ciascuna restituiscono il valore randomizzato e un nuovo generatore con un nuovo seme (basata su quella precedente). Per ottenere ogni volta un nuovo valore, la catena di nuovi generatori (gen1, gen2, gen3) deve essere passata. I generatori impliciti usano solo una variabile interna per memorizzare i valori gen1 .. in background.

Fare questo manualmente è un dolore, e in Haskell è possibile utilizzare una monade di stato per renderlo molto più semplice. Dovrai implementare qualcosa di meno puro o utilizzare una funzione come monade, frecce o valori di unicità per astrarla.

Ottenere il tempo di sistema è impuro perché il tempo potrebbe essere diverso ogni volta che si chiede.

La modalità di sospensione è più confusa perché il sonno non influisce sul risultato della funzione e si potrebbe sempre ritardare l'esecuzione con un ciclo occupato e ciò non influisce sulla purezza. Il fatto è che il sonno è fatto per il bene di qualcos'altro, che è un effetto collaterale.

L'allocazione della memoria in lingue pure deve avvenire in modo implicito, poiché allocare e liberare esplicitamente memoria sono effetti secondari se è possibile eseguire qualsiasi tipo di confronto tra puntatori. In caso contrario, la creazione di due nuovi oggetti con gli stessi parametri produrrebbe comunque valori diversi perché avrebbero identità diverse (ad esempio non essere uguali all'operatore == di Java).

So che ho vagabondato un po ', ma spero che questo spieghi quali sono gli effetti collaterali.

+0

Sono ben consapevole di quale sia l'effetto collaterale. Il mio punto era che gli effetti collaterali non sono del tutto ben definiti. Nota che sto parlando un po 'più liberamente della definizione Haskellesca che hai dato sopra. La mia lingua NON sarà pura. Ciò significa che chiamare la stessa funzione due volte non è mai garantito essere indifferente. Ciò che è importante per me nel mio design è semplicemente il contenimento degli effetti collaterali. Esiste un mondo di differenza tra la filosofia di Haskell che ogni valore è immutabile e semplicemente impedisce a qualsiasi vecchio Joe di lanciare i suoi missili. –

4

Il problema di come descrivere e controllare gli effetti sta attualmente occupando alcune delle migliori menti scientifiche nei linguaggi di programmazione, tra cui persone come lo Greg Morrisett dell'Università di Harvard. Per quanto ne so, il lavoro pioneristico più ambizioso in questo campo è stato fatto da David Gifford e Pierre Jouvelot nel linguaggio di programmazione FX iniziato nel 1987. Lo language definition è online, ma è possibile ottenere maggiori informazioni sulle idee leggendo il loro 1991 POPL paper.

2

Questa è una domanda davvero interessante, e rappresenta uno degli stadi che ho attraversato e, francamente, è andato oltre.

Ricordo seminari in cui Carl Hewitt, parlando del suo formalismo di attori, ne discusse. La definì in termini di un metodo che forniva una risposta che era unicamente una funzione dei suoi argomenti, o che poteva dare risposte diverse in momenti diversi.

Dico che mi sono spostato oltre perché rende il linguaggio stesso (o il modello computazionale) il soggetto principale, in contrapposizione al/i problema/i che dovrebbe risolvere. Si basa sull'idea che il linguaggio dovrebbe avere un modello di base formale in modo tale che le sue proprietà siano facilmente verificabili. Va bene, ma rimane comunque un obiettivo lontano, perché non esiste ancora una lingua (per quanto ne so) in cui la correttezza di qualcosa di semplice come bubble sort è facile da provare, per non parlare di sistemi più complessi.

Quanto sopra è un obiettivo fine, ma la direzione che ho seguito è stata quella di esaminare i sistemi di informazione in termini di teoria delle informazioni . In particolare, supponendo che un sistema inizi con un corpus di requisiti (su carta o in testa di qualcuno), tali requisiti possono essere trasmessi a una macchina di scrittura del programma (sia automatica che umana) per generare codice sorgente per un'implementazione funzionante. ALLORA, quando le modifiche si verificano ai requisiti, le modifiche vengono elaborate come modifiche delta al codice sorgente dell'implementazione.

Quindi la domanda è: quali proprietà del codice sorgente (e del linguaggio in cui è codificato) facilitano questo processo? Chiaramente dipende dal tipo di problema da risolvere, dal tipo di informazioni che vanno e vengono (quando), da quanto tempo devono essere conservate le informazioni e dal tipo di elaborazione che deve essere eseguita su di esse. Da questo si può determinare il livello formale della lingua necessaria per quel problema.

ho realizzato il processo di avviamento attraverso modifiche delta di requisiti al codice sorgente è reso più facile in quanto il formato del codice viene più a assomigliano i requisiti, e non v'è un modo quantitativo piacevole per misurare questo resemblence, non in termini di somiglianza superficiale, ma in termini di azioni di modifica. La nota tecnologia che esprime al meglio questo è un dominio specifico (DSL). Così ho capito che quello che cerco di più in un linguaggio generico è la capacità di creare linguaggi speciali.

A seconda dell'applicazione, tali linguaggi di scopo speciale possono o non possono richiedere specifiche caratteristiche formali come notazione funzionale, controllo degli effetti collaterali, paralellismo, ecc. In effetti, ci sono molti modi per creare un linguaggio speciale, dall'analisi, all'interpretazione, alla compilazione, fino alla semplice macro in un linguaggio esistente, fino alla semplice definizione di classi, variabili e metodi in una lingua esistente. Non appena dichiari una variabile o subroutine, crei un nuovo vocabolario e quindi una nuova lingua in cui risolvere il tuo problema. In effetti, in questo senso generale, non penso che sia possibile risolvere qualsiasi problema di programmazione senza essendo, a un certo livello, un progettista di linguaggi.

Quindi, buona fortuna, e spero che apra nuove prospettive per voi.

+0

Grazie per la risposta. Vedrò se non riesco a trovare nulla sui seminari di Carl Hewitt. Non direi che il linguaggio stesso è il punto centrale. Non ho davvero idea del tipo di linguaggio che sto per scrivere ancora. Potrei finire per concentrarmi sulla scrittura di una VM invece di una lingua e poi inventare un semplice linguaggio dimostrativo per mostrare il suo merito. DSL e problemi di produttività linguistica non sono molto importanti in questa fase (alla fine, ma non ancora!), Ma i miei modelli di threading e memoria sono estremamente importanti. –

+0

Buona fortuna. Ecco un inizio: http://en.wikipedia.org/wiki/Carl_Hewitt Lui è un tipo intelligente. –