Novembre 2018
Volume 33 Numero 11
.NET - creare un proprio linguaggio di Script con i delegati simbolici
Dal Thomas Hansen | Novembre 2018
Mi piace il C# compilatore. Mia fidanzata attestazioni mi piace più di mi piace l'utente, anche se, per ovvie ragioni, non sarebbe mai ammettere che pubblica in. Tipizzazione forte, generics, LINQ, operazione di garbage collection, interfaccia della riga di comando, l'elenco di interessanti tratti vengono inseriti in. Tuttavia, ogni ora e quindi necessari un paio di giorni disattivata dal titoli dimestichezza con la quale il compilatore costituisce in genere me. Per i giorni, si sceglie di codice in linguaggi di programmazione dinamici, ovvero più dinamici e la lingua, il più pericolosi e divertente.
OK. Sufficiente introduzione. Ora è possibile eseguire operazioni quali i programmatori reali eseguire questa operazione quando verrà lasciata invariata, passiamo alla creazione di codice:
Salve, mondo
Che cosa? Pensavi che ho detto "code"? Yup, ho fatto certamente e che il testo è effettivamente codice. Per comprendere, vediamo operazioni possibili con questo codice. Dare un'occhiata figura 1, che mostra un esempio di ciò che faccio riferimento come delegati simbolici minimalistici. Creare un'app console vuota in Visual Studio e digitare il codice dal figura 1.
Figura 1 esempio Minimalistici dei delegati simbolici
using System;
using System.Collections.Generic;
class MainClass
{
public static void Main(string[] args)
{
// The "programming language"
var keywords = new Dictionary<string, Action> {
// The "hello" keyword
["hello"] = () => {
Console.WriteLine("hello was invoked");
},
// The "world" keyword
["world"] = () => {
Console.Write("world was invoked");
}
};
// The "code"
string code = "hello world";
// "Tokenising" the code
var tokens = code.Split(' ');
// Evaluating the tokens
foreach (var token in tokens) {
keywords[token]();
}
}
}
Solo 25 righe di codice è stato creato un linguaggio di programmazione micro. Per comprendere i vantaggi, tenere presente che la variabile "code" può essere inviata tramite rete; possono essere recuperato da un database; possono essere archiviato in un file. e posso modificare dinamicamente la stringa da "hello world" per "hello world" o "hello world world hello", d'altra parte e modificare completamente il risultato a cui viene restituito. Questa capacità significa che è possibile combinare in modo dinamico i delegati per finire con un elenco di oggetti funzione, in modo sequenziale valutata, in base al contenuto del codice. Un linguaggio di programmazione compilato in modo statico improvvisamente ho trasformato in un linguaggio di scripting dinamico, è sufficiente, la suddivisione di una frase in parole relativo e usando le singole parole come chiavi di ricerca in un dizionario. Il dizionario figura 1, di conseguenza, diventa un "linguaggio di programmazione". In effetti, è un delegato simbolico.
In questo esempio è destinato esclusivamente a illustrare l'idea di base, è ovviamente non vale interessante. Voglio creare qualcosa di leggermente più interessanti da ricerca di un po' ulteriormente queste idee tana del coniglio, come illustrato nella figura 2.
Figura 2 i delegati in modo dinamico concatenamento
using System;
using System.Linq;
using System.Collections.Generic;
public class Chain<T> : List<Func<T, T>>
{
public T Evaluate(T input)
{
foreach (var ix in this)
{
input = ix(input);
}
return input;
}
}
class MainClass
{
public static void Main(string[] args)
{
var keywords = new Dictionary<string, Func<string, string>>
{
["capitalize"] = (input) =>
{
return input.Replace("e", "EE");
},
["replace"] = (input) =>
{
return input.Replace("o", "0");
}
};
string code = "capitalize replace";
var tokens = code.Split(' ');
var chain = new Chain<string>();
chain.AddRange(tokens.Select(ix => keywords[ix]));
var result = chain.Evaluate("join the revolution, capitalize and replace");
Console.WriteLine(result);
}
}
Ora ho un elemento che sembra quasi particolarmente utile, ovvero la possibilità di dinamicamente catena insieme i delegati, determinando in un oggetto lambda di loosely coupled funzioni. In questo modo il codice dichiara una catena di funzioni che trasforma un oggetto in modo sequenziale, in base ai simboli che decido di inserire nel mio codice. Per il record, è in genere non deve ereditare dall'elenco, ma per brevità, che ho deciso di eseguire questa operazione per illustrare il concetto principale dell'esempio.
L'espansione su questo concetto è semplice. L'esercizio consiste nel trovare il denominatore comune più piccolo per un delegato generico che può descrivere qualsiasi costrutto di programmazione si conosce, che fossero risulta essere il seguente delegato:
delegate object Function(List<object> arguments);
Questo delegato può rappresentare quasi ogni struttura programmazione inventato che mai. Tutti gli elementi nei sistemi informatici possono accettare un elenco di argomenti di input e restituire un valore al chiamante. Questo delegato è la definizione di base di input/output per tutte le idee di calcolo molto e diventa una struttura di programmazione atomica che è possibile usare per risolvere i problemi riscontrati dagli utenti.
Soddisfare Lizzie
Come ho scritto questo articolo, ho creato un linguaggio di programmazione ad esempio l'idea precedente. Ho scritto la lingua intera, che viene chiamato Lizzie: dopo la mia fidanzata, Lisbeth, ovvero in un paio di tutte le prove, Luna piena sorge i fine settimana. Il linguaggio è contenuto interamente in un singolo assembly, circa 2.000 righe di codice. Quando viene compilato, è solo superiore a 45KB sul disco e il compilatore"" è solo 300 righe di C# codice. Lizzie è anche facile da estendere e permette a chiunque aggiungervi i propri "keywords", che consente di creare facilmente il proprio linguaggio specifico di dominio (DSL). Un caso d'uso per un linguaggio di questo tipo è un motore basato su regole, in cui è necessario unire più dinamicamente codice rispetto a C# consente di. Con Lizzie è possibile aggiungere il codice di script dinamico per il compilate in modo statico C# applicazione di farmi frammenti di codice di funzionalità. Lizzie consiste nel C# quali spice è per la cena. Non si vuole solo spice mangiare, ma se si aggiunta alcuni spice al steak, l'esperienza diventa ovviamente molto più piacevole. Per provare Lizzie out, creare un'applicazione console vuota in C#, aggiungere Lizzie come pacchetto NuGet e usare il codice nel figura 3.
Figura 3 Creazione di un linguaggio specifico di dominio
using System;
using lizzie;
class Demo1
{
[Bind(Name = "write")]
object write(Binder<Demo1> binder, Arguments arguments)
{
Console.WriteLine(arguments.Get(0));
return null;
}
}
class MainClass
{
public static void Main(string[] args)
{
var code = "write('Hello World')";
var lambda = LambdaCompiler.Compile(new Demo1(), code);
lambda();
}
}
In 22 solo righe di codice senza dubbio ho creato il proprio linguaggio DSL e aggiunta di una parola chiave specifico di dominio del linguaggio.
La funzionalità principale di Lizzie è che è possibile associare il codice Lizzie a un tipo di contesto. Il metodo LambdaCompiler.Compile figura 3 è, in realtà, un metodo generico, ma il relativo argomento di tipo viene dedotto automaticamente dal primo argomento. Internamente, Lizzie verrà creato uno strumento di associazione che si esegue l'associazione al tipo, rendendo disponibili tutti i metodi con l'attributo di associazione per l'utente dal codice Lizzie. Quando il codice Lizzie viene valutato, dispone di una parola chiave aggiuntiva denominata "scrittura". È possibile associare qualsiasi metodo al codice Lizzie, purché disponga della firma corretta. Ed è possibile associare il codice Lizzie a qualsiasi tipo.
Lizzie ha diverse parole chiave predefinite, che rende disponibile per l'utente per il proprio codice, ma non è necessario scegliere questa opzione se non si vuole. Figura 4 Mostra un esempio più completo che usa alcune di queste parole chiave.
Figura 4 uso di alcune parole chiave predefinita
using System;
using lizzie;
class Demo1
{
[Bind(Name = "write")]
object write(Binder<Demo1> binder, Arguments arguments)
{
Console.WriteLine(arguments.Get(0));
return null;
}
}
class MainClass
{
public static void Main(string[] args)
{
var code = @"
// Creating a function
var(@my-function, function({
+('The answer is ', +(input1, input2))
}, @input1, @input2))
// Evaluating the function
var(@result, my-function(21,2))
// Writing out the result on the console
write(result)
";
var lambda = LambdaCompiler.Compile(new Demo1(), code);
lambda();
}
}
Il codice Lizzie figura 4 crea prima di tutto una funzione denominata "my-function", quindi richiama tale funzione con due argomenti integer. Infine, scrive il risultato della chiamata di funzione nella console. Con 21 righe di C# il codice e otto righe di codice Lizzie, ho ho valutato un frammento di codice dinamico che consente di creare una funzione in un linguaggio di scripting dinamico, dall'interno personali C# codice, durante l'aggiunta di una nuova parola chiave per il linguaggio di scripting di utilizzo. Solo 33 righe di codice sono necessari in totale, inclusi i commenti. Queste 33 righe di codice consentono di aver creato il proprio linguaggio di programmazione di attestazione. Anders Hejlsberg, spaziale e lasciare Jimmy little assumere...
È un linguaggio di programmazione "Real" Lizzie?
Per rispondere a questa domanda, è necessario considerare ciò che è prendere in considerazione un linguaggio di programmazione reale sia. Lizzie è farmi e, almeno in teoria, consente di risolvere ogni problema di calcolo che si possa immaginare. Pertanto, in base alla definizione formale di cosa si intende per "real" linguaggio di programmazione, è certamente reale come qualsiasi altro linguaggio di programmazione. D'altra parte, non è né interpretato né compilato, perché ogni chiamata di funzione è semplicemente una ricerca nei dizionari. Inoltre, include solo un numero limitato di costrutti e tutto ciò che è incentrata sulla sintassi "function(arguments)". In effetti, anche se le istruzioni seguono la sintassi della funzione definita in precedenza nel delegato generico:
// Creates a function taking one argument
var(@foo, function({
// Checking the value of the argument
if(eq(input, 'Thomas'), {
write('Welcome home boss!')
}, {
write('Welcome stranger!')
}) // End of if
}, @input)) // End of function
// Invoking the function
foo('John Doe')
The syntax of if is as follows:
if(condition, {lambda-true}, [optional] {lambda-else})
Il primo argomento alla parola chiave "if" è una condizione. Il secondo argomento è un blocco di espressione lambda viene valutato se la condizione restituisce (true) diverso da null. Il terzo argomento è un blocco di espressione lambda facoltativo che viene valutato se la condizione restituisce un valore null (false). In modo che la parola chiave "if" è in realtà una funzione a cui è possibile fornire un argomento di espressione lambda, usando il "{...... codice}" sintassi per dichiarare l'espressione lambda. Questo potrebbe risultare leggermente strano all'inizio, perché tutto avviene tra la parentesi e parentesi di chiusura delle parole chiave, a differenza di altri linguaggi di programmazione che usano una sintassi più tradizionale. Tuttavia, per creare un compilatore del linguaggio di programmazione in 300 righe di codice, alcune decisioni in grassetto semplicemente dovevano essere apportate. E Lizzie riguarda la semplicità.
Funzioni di Lizzie sono sorprendentemente simili alla struttura di un'espressione s dalla Lisp, anche se senza la notazione polacca strana. Perché un'espressione s può descrivere un valore e le funzioni di Lizzie sono espressioni semplicemente s con il simbolo (primo argomento) all'esterno le sue parentesi, è possibile descrivere tutto ciò con Lizzie. Ciò probabilmente Lizzie si trasforma in un'implementazione dinamica di Lisp per Common Language Runtime (CLR), con una sintassi più intuitiva per C#gli sviluppatori /JavaScript. Consente di aggiungere il codice dinamico nella parte superiore di compilate in modo statico C#, senza dover leggere migliaia di pagine della documentazione per informazioni su un nuovo linguaggio di programmazione. In effetti, la documentazione completa per Lizzie è solo 12 pagine di testo, che significa che uno sviluppatore di software disponibili letteralmente Lizzie in circa 20 minuti.
Lizzie: JSON per il codice
Una delle mie funzionalità preferite di Lizzie è la mancanza di funzionalità. Vorrei illustrare questo concetto con un elenco parziale delle operazioni che non è possibile eseguire Lizzie. Non è possibile Lizzie:
- leggere o scrivere dal file system
- eseguire query SQL
- ti chiediamo è la password
- modificare lo stato del computer in uso affatto
In effetti, un frammento di codice Lizzie impostazione predefinita non può essere dannoso, non lo è nemmeno in teoria. Questa mancanza di offre le funzionalità Lizzie alcune capacità univoca che Roslyn e C# creazione di script non forniscono.
Nello stato originale, Lizzie è considerato sicura, consentendo di trasmettere in modo sicuro codice in una rete, da un computer a un altro computer, simile a quello JSON consente di trasmettere i dati. In corrispondenza dell'endpoint che accetta il codice Lizzie, è quindi necessario implementare in modo esplicito il supporto per le funzioni è necessario codice Lizzie ad accedere a, in modo da consentire il funzionamento per il caso d'uso. Potrebbe trattarsi di un C# metodo che legge dati da un database SQL o la possibilità di aggiornare i dati in un database SQL o di leggere o scrivere i file. Tuttavia, tutte queste chiamate di funzione può essere ritardate fino a quando non dopo essersi assicurati che il codice tenta di eseguire qualsiasi elemento sta provando a eseguire è in effetti autorizzato a eseguire questa operazione. Di conseguenza, è possibile implementare facilmente l'autenticazione e autorizzazione prima di consentire a, ad esempio "insert sql", "file di lettura" o qualsiasi altro elemento.
Questa proprietà di Lizzie consente di creare un endpoint REST HTTP generico in cui il livello client invia il codice Lizzie e un server in cui viene quindi valutato. È quindi possibile avere il server crea una risposta JSON che invia al client. E l'aspetto più interessante, è possibile implementare in modo sicuro. È possibile implementare un singolo endpoint REST HTTP che accetta solo le richieste POST contenente codice Lizzie e sostituire letteralmente intero back-end con un analizzatore Lizzie pari al 100% dinamico e generico. In questo modo è possibile spostare la logica di business intera e accesso ai dati livello nel codice front-end, in modo che il codice di front-end consente di creare dinamicamente codice Lizzie che trasmette al server per la valutazione. E, è possibile farlo in modo sicuro, presupponendo che si autenticare e autorizzano i client prima di consentire loro di valutare il codice Lizzie.
In sostanza, intero, improvvisamente, con facilità la compilazione dell'applicazione in JavaScript o TypeScript o ObjectiveC o qualsiasi elemento, ed è possibile creare client in qualunque linguaggio di programmazione si desidera che in modo dinamico che consente di creare codice Lizzie e inviare questo codice al server.
Lezioni da Einstein
Quando Albert Einstein preso nota il suo famosa equazione per spiegare i nostri Universe (universo), che aveva solo tre componenti semplici: energia, massa e la velocità della luce elevato al quadrato. Che equazione, potrebbe essere facilmente comprensibile 14 dell'anno precedente con ragionevole. é quindi dei calcoli matematici. Informazioni sulla programmazione informatica, d'altra parte, richiede migliaia di pagine e milioni in caso contrario trilioni di parole, gli acronimi, i token e simboli, oltre a un'intera sezione WikiPedia su paradigmi di numerosi modelli di progettazione e una vasta gamma di linguaggi, ciascuno con le strutture completamente diverse e idee, ognuno dei quali richiedono "fondamentale" Framework e librerie che è necessario aggiungere a una "soluzione" prima di iniziare funzionante per il problema di dominio. E vanno considerati conoscere tutte queste tecnologie da cuore prima di poter avviare che fa riferimento a se stessi come sviluppatore software intermedi. Si è l'unico che visualizza il problema?
Lizzie non è un elenco puntato magic, né le simbolici delegati, ma sono sicuramente un passo nella direzione di "20 Vai a 10." E in alcuni casi, per spostarsi in avanti, è necessario avviare da un passo indietro. In alcuni casi è necessario osservare neutrally manualmente dall'esterno. Se, come una community di professionisti, procedere, è possibile tenere tuttavia presente che la soluzione per i nostri come direbbe re Lear corrente è la semplicità e non 50 più modelli di progettazione, eseguire una query 15 nuovi linguaggi, 100 nuove funzionalità del linguaggio e un milione nuove librerie e Framework, ognuno con un trilioni spostamento di parti.
Minore è che altre informazioni, sempre, pertanto la visualizzazione di informazioni meno e meno informazioni! Se si desidera minore, andiamo a github.com/polterguy/lizzie. Dove è possibile trovare Lizzie, con zero indipendenza dai tipi, nessuna parola chiave, nessun operatore, nessun OOP e certamente non quanto è un framework in vista o una singola libreria.
Conclusioni
La maggior parte del settore informatico tende a non sono d'accordo con la maggior parte del mio idee. Se richiesto il software medio progettare che ritiene su questi concetti, egli genererebbe probabilmente librerie intere nel quadrante del come origini di dimostrare come errato sono. Ad esempio, l'ipotesi più comuni nello sviluppo del software è che la tipizzazione forte è valida e tipizzazione debole è valido. Per l'utente corrente, tuttavia, la semplicità è il gioco solo in Town (città), anche quando è necessario generare la tipizzazione forte la finestra. Tenere presente, tuttavia, che consentono le idee, ad esempio Lizzie di "vivacizzare" esistente in modo statico di tipo C# il codice, non sostituirlo. Anche se è mai usare i concetti di scrittura di codice presentati in questo articolo direttamente, comprendere che i concetti principali consentono di scrivere codice standard in un linguaggio di programmazione tradizionali in modo più efficace e Guida in linea si lavora raggiungimento dell'obiettivo di semplicità.
Una lezione di cronologia di programmazione
Quando era uno sviluppatore junior, ho utilizzato per creare applicazioni a 3 livelli. L'idea era di separare i livelli di dati dei livelli per la logica di business e i livelli dell'interfaccia utente. Il problema è che, man mano che aumentano Framework front-end in termini di complessità, si è costretti a creare un'architettura di applicazione livello 6. Prima di tutto è necessario creare un'architettura a 3 livelli sul lato server, quindi un'architettura client-side a 3 livelli. E, come se che non erano sufficienti, quindi è necessario convertire il codice in Java, ObjectiveC o qualsiasi elemento supportare tutti i client possibili disponibili. Essere d'impatto, in questo caso, ma questo tipo di architettura è che chiameremo "insanity-driven design" poiché aumenta la complessità delle applicazioni al punto in cui siano quasi impossibile da gestire. Una singola modifica nell'interfaccia utente di front-end spesso viene propagato lungo 15 livelli di architettura e diversi linguaggi di programmazione ed è necessario apportare modifiche in tutti i numerosi livelli solo per aggiungere una colonna semplice a una griglia dati del front-end. Lizzie risolvere il problema, consentendo di front-end per l'invio di codice al back-end, back-end della valuta e restituisce al client come JSON. Certo, si perde l'indipendenza dai tipi, ma quando l'indipendenza dai tipi comporta la necessità di unire i diversi livelli delle applicazioni, in modo che le modifiche in un'unica posizione vengono propagate a tutti gli altri livelli del progetto, il costo dell'indipendenza dai tipi è semplicemente non vale la pena prestare.
Iniziare a scrivere il quando ero 8 anni, su un 1 Oric nel 1982. Mi ricordo chiaramente il primo programma che è stato scritto. È verificato un errore simile al seguente:
10 PRINT "THOMAS IS COOL"
20 GOTO 10
Se un bambino di 8 anni oggi vuole riprodurre tale esperienza acquisita, seguire tutte le procedure consigliate, usando un framework lato client, ad esempio Angular e un framework di back-end, ad esempio .NET, probabilmente questo elemento kid dovranno conoscere migliaia di pagine di scienze informatiche tecnici documentazione da cuore. Al contrario, ho iniziato con un libro che era circa 300 pagine e un numero limitato di riviste. Prima che rendeva alla pagina di 100, ho avevo creato un gioco per computer, l'età di 8. Spiacenti se ciò mi rende sembrare precedente, ma non si tratta evoluzione e analisi utilizzo software, si tratta come direbbe re Lear e "per la devoluzione". E, Sì, prima di avviare frenetically annotare gli obiezioni, questo problema è stato scientificamente analizzato e neutrally osservato e confermato, professori e ricercatori, tutte le più brillanti di me.
Il fatto è che tale computer di programmazione professione (risorse umana) si trova il brink di estinzione, poiché la complessità che viene continuamente aggiunte a breve potrebbe raggiungere il punto in cui supera la capacità del cervello umano per creare programmi per computer. Poiché la programmazione sta diventando così impegnativa dal punto di vista cognitivo, potrebbe essere che nessun essere umano sarà in grado di farlo 10 anni dopo. Allo stesso tempo, esseri umani stanno diventando sempre più dipendenti nei computer e software ogni giorno. Chiamami vecchio modello in questo caso, ma un tipo di, ad esempio l'idea che un essere umano in una posizione disponibili sia in grado di comprendere le operazioni che sono fondamentali ai miei felicità, l'esistenza e la qualità della vita.
Thomas Hansenè stata la creazione di software da quando ha 8 anni fa, quando ha iniziato a scrivere codice usando il computer Oric-1 nel 1982. Davide fa riferimento a se stesso come programmatore che cerca di ridurre la complessità di programmazione moderna e rifiuta a riconoscere eventuali convinzione in dogmas tecnologico computer Zen. Hansen funziona per il codice di luminosità Cipro in cui crea software Tecnofinanza.
Grazie al seguente esperto tecnico Microsoft per la revisione dell'articolo: James McCaffrey