Non proprio. L'interoperabilità linguistica è un problema difficile e l'inclusione del linguaggio ancora di più.
Molte lingue hanno costrutti di sintassi non banali che non possono essere facilmente analizzati da un parser generico. Perl 5 ne è l'esempio principale: per analizzare un programma Perl 5, è necessario eseguirlo. Per esempio. ecco un programma Perl 5 che si lamenterà di una parentesi di chiusura extra il 50% delle volte:
BEGIN {
print "Running arbitrary code during parsing :)\n";
*f = rand() > 0.5 ? sub () {} : sub ($) {};
}
for ("the lulz") {
f /7 } # /
}
Come funziona? Definiamo una funzione f
con un prototipo. Il prototipo influisce sull'analisi degli argomenti. Il prototipo ($)
analizza un argomento, che sarà il risultato dell'applicazione della regex /7 } # /
rispetto a $_
. Ma il prototipo ()
non analizza alcun argomento, quindi f /7
è una divisione e # /
è un commento.
Considera anche che alcuni linguaggi come la famiglia Lisp hanno un concetto completamente diverso di "sintassi" rispetto ai linguaggi di parentesi graffa simili a C. I linguaggi sensibili all'indentazione come Python o Haskell non hanno rappresentazione in BNF. Quindi l'incorporamento sintattico è davvero difficile. Invece, l'incorporamento userà tipicamente qualche tipo di stringa o sintassi di here-doc.
Il problema più difficile è l'interoperabilità semantica. Questo non è fondamentalmente un problema risolvibile, perché le lingue sono follemente diverse. Ci sono semplici considerazioni come la gestione della memoria. Se si passa un oggetto raccolto in una lingua con gestione manuale della memoria, come verrà smaltito? L'oggetto potrebbe ora essere referenziato dal codice in entrambe le lingue e il garbage collector potrebbe non essere in grado di vedere alcuni riferimenti.
I sistemi oggetto sono un'altra area di grandi differenze. In JavaScript e in molti altri linguaggi dinamici, posso applicare i metodi al runtime. Come può funzionare in C ++ dove le chiamate al metodo possono essere risolte al momento della compilazione? In Java, l'ordine di inizializzazione degli oggetti è effettivamente genitore-prima-figlio. In che modo le classi Java esistenti possono funzionare quando fanno parte di un progetto di ereditarietà multipla in Python? C non ha nemmeno un sistema di oggetti di prima classe.
Ci sono anche alcune piccole differenze di sistema di tipo gazillion. Una lingua ha stringhe immutabili, un'altra no. In C o C ++, non è possibile chiamare determinate funzioni a meno che non si disponga di un oggetto non const. Il supporto per le funzioni variadic è incoerente, variando tra "l'unico tipo di funzione" (Perl) su "sintassi speciale per un argomento di matrice" (Java) su "espansione del modello in fase di compilazione" (C ++). Alcune lingue hanno una sorta di namespace, altre no. Alcune lingue supportano la semantica del valore per i tipi definiti dall'utente. I generici differiscono selvaggiamente. Funzioni sovraccaricate. E così via.
Il risultato di queste incompatibilità è che deve esserci un qualche livello di interfaccia. Molto spesso, questo deve essere esteso per i tipi definiti dall'utente. O gli oggetti vengono convertiti in una rappresentazione diversa oppure sono rappresentati da una sorta di oggetto wrapper nella lingua di destinazione. Chiamare una funzione non tipizzata, variadica da un linguaggio statico spesso finisce per essere piuttosto scomodo. Non sembrerà mai nativo.
Ci sono esempi specifici in cui un qualche tipo di interoperabilità funziona bene. Ad esempio, la generazione automatica di binding di lingua da una definizione di origine come SWIG. Alcune piattaforme linguistiche come CLR e JVM definiscono un modello semantico comune che consente una facile interoperabilità per tutte le lingue che utilizzano questa piattaforma.
Esistono anche alcuni approcci all'interoperabilità che non sono invisibili, ma funzionano comunque bene. Ad esempio gli FFI o il modello a oggetti componenti. Ma questi generalmente hanno severe restrizioni semantiche.
Pertanto ti esorto ad abbandonare la tua ricerca di perfetta interoperabilità linguistica tra una vasta gamma di lingue. Invece, potresti avere più fortuna nel migliorare l'interoperabilità tra due lingue specifiche, ad es. "R in Python" o somesuch. In generale, sarà molto più difficile interfacciare due lingue quando ognuna di esse definisce la propria VM e più semplice se le lingue possono comunicare attraverso un'interfaccia semplice come la convenzione di chiamata C.
In pratica, l'interoperabilità linguistica è così terribilmente difficile che l'interoperabilità in-process è spesso evitata del tutto (a meno che non condividano una piattaforma comune, o una delle lingue sia C). Invece, la comunicazione tra processi su un socket o pipe risulta molto più semplice. Vedi anche: filosofia Unix, microservizi.