alvisespano / Polygen

The famous random sentence generator.
Other
39 stars 9 forks source link

Cross-compilare OCaml a Javascript #10

Open tajmone opened 6 years ago

tajmone commented 6 years ago

Ho rispulciato i miei appunti riguardo la cross-compilazione a Javascript. E, indovina un po'? OCaml è supportato.

Questa è la lista di compilatori OCaml 2 Javascript che ho trovato sul Wiki di CoffeeScript:

Name Description
Ocamljs OCaml to JS.
O’Browser OCaml bytecode interpreter in JS.
Js_of_ocaml OCaml bytecode to JS compiler.
Bucklescript OCaml to readable JS

Il potenziale direi che è enorme!

Dato che Polygen non si appoggia a librerie esterne, basterebbero poche modifiche per trasformarlo in una libreria, sia binaria che Javascript.

Una volta compilatolo in Javascript, esistono ulteriori strumenti per poterne fare sia un pacchetto NPM per Node.js (come libreria e come struemnto CLI 100% javascript), sia per renderlo utilizzabile tramite browser:

Questa presentazione a slide è un ottimo punto di partenza:

Che ne pensi?

Io credo che riscrivere tutto da zero sia un mega lavoraccio, perlopiù inutile. La cross-compilazione mi pare più fattibile e consentirebbe di preservare il codice attuale.

tajmone commented 6 years ago

Sto spulciando i progetti OCaml menzionati nella tabella; alcuni sono veri e propri backend per il compilatore OCaml, altri sono virtual machine che consentono di far girare codice oggetto OCaml nel browser.

Sono davvero progetti fantastici, e sembra potrebbero essere in grado di compilare PolyGen senza ulteriori strumenti.

Javascript è un linguaggio davvero performante, ed è molto più universale che non il Java (a mio avviso). Una volta creata la libreria di PolyGen (libPolyGen?), con una sua API, il resto sarebbe una passeggiata.

Le librerie statiche e dinamiche potrebbero essere incorporate in applicazioni di vario tipo, il che consentirebbe anche di implementare una nuova interfaccia da riga di comando in qualsiasi linguaggio (che so, per aggiungere nuove opzioni e funzionalità, filtri, ecc.).

La libreria in Javascript consentirebbe di utilizzare Polygen direttamente nel browser, buttando il carico di lavoro sul client e scavalcando la necessità di un server, interfacciamenti CGI, PHP, ecc. La pagina web si limiterebbe a fornire il sorgente javascript, ma siccome le risorse di calcolo passano al client, anche se 10.000 utenti in contemporanea continuassero a generare testo il server non consumerebbe alcuna risorsa.

alvisespano commented 6 years ago

Di riscriverlo in javascript non ci penso neanche, perché non solo è un lavoraccio, ma javascript è anche il peggior linguaggio della storia dell'umanità e non ho intenzione di riscrivere un programma pulito, piccolo ed intricato come il polygen in un linguaggio unsound. La cross compilazione mi sembra la strada migliore, perché si sporca le mani al posto mio. Oltre ad essere brutto javascript è anche lento, non è vero che è performante se paragonato ai linguaggi con un compilatore di qualità (come C per esempio, o lo stesso OCaml). Tuttavia - forse tu intendi - è performante in senso relativo, cioè, per essere un pastrocchio interpretato che gira su un browser, javascript non è male; e negli ultimi anni ha anche fatto qualche piccolo passo in avanti, sia in termini di performance dei jit sia in termini di scelte di language design. Morale della favola: della performance possiamo tranquillamente fregarcene, perché anche se è un ordine di grandezza più lento di OCaml, ci va bene lo stesso. Inoltre, Javascript cross-compilato da OCaml è talmente una buona notizia che si può sorvolare sopra l'orrore e sopra la performance; performance che tra l'alto impatta solo sul client in ogni caso.

Il problema è che la cross-compilazione è una bestia strana: bisogna scoprire COSA cross-compila e cosa produce in output. Se è in grado di tradurre i binding a top level di un modulo ML in binding a top level js allora direi che è meglio prima scrivere la libreria in OCaml (così il refactoring ed il testing è fatto in linguaggio migliore) e poi la tradurla; se invece è in grado di tradurre applicazioni stand alone (cioè aventi un main o una qualche forma di entrypoint) allora sarà necessario mettere mano al sorgente js generato dal traduttore per rendere il tutto una libreria con una API -- e questo potrebbe essere una passeggiata come no, dipende da quanto traduce e da quali sono le limitazioni che impone (perché sicuramente non compilerà ogni singola feature di OCaml, poiché alcune di esse non sono nemmeno esprimibili in js, se non riproducendole tramite un qualche pattern js in cui si perde probabilmente il fulcro stesso della feature).

Infine, vorrei pensare ad ultimo aspetto: la mantenibilità. Ora come ora Polygen è 1 programma standalone scritto in 1 linguaggio (OCaml). Se domani mattina lo cross-compiliamo in js, al netto del problema di refactoring di cui sopra, sarà necessario mantenere 2 distinte versioni del sorgente. Naturalmente potremmo dismettere totalmente la versione OCaml e tenere solamente quella js, ma questo significa che in futuro, qualunque siano gli interventi e gli upgrade al polygen, dovranno essere implementati (a) in javascript, (b) intervenendo in un sorgente che non è stato scritto da un umano e (c) usando un linguaggio pessimo e per niente adatto alla scrittura di software di genere term manipulation. Questi sono 3 grossi ostacoli che possono essere sormontati tutti contemporaneamente se invece prediamo un'altra strada: continuiamo a mantenere ed upgradare polygen in OCaml ed introduciamo la cross-compilazione nel processo di building. In altre parole: il programma (o libreria, o entrambi) rimane bello e pulito, scritto in OCaml, e la versione js la generiamo a build time ogni volta. Per poter imboccare questa strada però occorre sapere come funziona la cross compilazione ml-to-js. E quindi ritorniamo al punto di prima.

C'è anche un'altra alternativa, sebbene meno realistica: potrei riscrivere Polygen in Haskell e mantenerlo per sempre in Haskell in ogni sua forma, libreria o altro, appoggiandomi al nutrito kit di strumenti per il web di cui Haskell e la sua community oggi dispongono. Haskell è un linguaggio che non fa rimpiangere OCaml (anzi, il suo type system è ancora più potente) e gode anche di ottima salute, quanto a ecosistema e supporto. Anche Scala sarebbe un candidato con grossomodo le medesime proprietà di Haskell. Si tratta però di lavoro più profondo ed intenso di quel "consolidamento" che avevo/avevamo in mente: per quanto polygen sia già scritto, tradurlo a mano significa riscriverlo secondo le convenzioni, gli standard e gli stili imposti dal linguaggio target. E non possiamo scendere a compromessi di qualità, altrimenti viene meno l'obiettivo primario del porting, ovvero la mantenibilità. Sì, insomma, è un lavoraccio pure questo alla fine della festa, e non sono sicuro di aver voglia :P

Il giorno 16 gennaio 2018 15:22, Tristano Ajmone notifications@github.com ha scritto:

Sto spulciando i progetti OCaml menzionati nella tabella; alcuni sono veri e propri backend per il compilatore OCaml, altri sono virtual machine che consentono di far girare codice oggetto OCaml nel browser.

Sono davvero progetti fantastici, e sembra potrebbero essere in grado di compilare PolyGen senza ulteriori strumenti.

Javascript è un linguaggio davvero performante, ed è molto più universale che non il Java (a mio avviso). Una volta creata la libreria di PolyGen ( libPolyGen?), con una sua API, il resto sarebbe una passeggiata.

Le librerie statiche e dinamiche potrebbero essere incorporate in applicazioni di vario tipo, il che consentirebbe anche di implementare una nuova interfaccia da riga di comando in qualsiasi linguaggio (che so, per aggiungere nuove opzioni e funzionalità, filtri, ecc.).

La libreria in Javascript consentirebbe di utilizzare Polygen direttamente nel browser, buttando il carico di lavoro sul client e scavalcando la necessità di un server, interfacciamenti CGI, PHP, ecc. La pagina web si limiterebbe a fornire il sorgente javascript, ma siccome le risorse di calcolo passano al client, anche se 10.000 utenti in contemporanea continuassero a generare testo il server non consumerebbe alcuna risorsa.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/alvisespano/Polygen/issues/10#issuecomment-357974813, or mute the thread https://github.com/notifications/unsubscribe-auth/ACcZxg67rMzP-wWyTxLNnSgug-5z5IDUks5tLLCWgaJpZM4Rf0md .

alvisespano commented 6 years ago

A scanso di equivoci: non voglio demolire la tua proposta, non fraintendermi :) Non amo i linguaggi come js, però le indagini che hai fatto hanno individuato una opportunità notevole con il cross compilatore ml-to-js. Se dobbiamo usare js a scatola chiusa (cioè solo come backend di una traduzione), allora ok, altrimenti potrebbe essere più complicato di quanto non sembri. Ma vale la pena informarsi meglio, appena ho tempo lo farò.

Il giorno 16 gennaio 2018 16:56, Alvise Spanò spano.alvise@gmail.com ha scritto:

Di riscriverlo in javascript non ci penso neanche, perché non solo è un lavoraccio, ma javascript è anche il peggior linguaggio della storia dell'umanità e non ho intenzione di riscrivere un programma pulito, piccolo ed intricato come il polygen in un linguaggio unsound. La cross compilazione mi sembra la strada migliore, perché si sporca le mani al posto mio. Oltre ad essere brutto javascript è anche lento, non è vero che è performante se paragonato ai linguaggi con un compilatore di qualità (come C per esempio, o lo stesso OCaml). Tuttavia - forse tu intendi - è performante in senso relativo, cioè, per essere un pastrocchio interpretato che gira su un browser, javascript non è male; e negli ultimi anni ha anche fatto qualche piccolo passo in avanti, sia in termini di performance dei jit sia in termini di scelte di language design. Morale della favola: della performance possiamo tranquillamente fregarcene, perché anche se è un ordine di grandezza più lento di OCaml, ci va bene lo stesso. Inoltre, Javascript cross-compilato da OCaml è talmente una buona notizia che si può sorvolare sopra l'orrore e sopra la performance; performance che tra l'alto impatta solo sul client in ogni caso.

Il problema è che la cross-compilazione è una bestia strana: bisogna scoprire COSA cross-compila e cosa produce in output. Se è in grado di tradurre i binding a top level di un modulo ML in binding a top level js allora direi che è meglio prima scrivere la libreria in OCaml (così il refactoring ed il testing è fatto in linguaggio migliore) e poi la tradurla; se invece è in grado di tradurre applicazioni stand alone (cioè aventi un main o una qualche forma di entrypoint) allora sarà necessario mettere mano al sorgente js generato dal traduttore per rendere il tutto una libreria con una API -- e questo potrebbe essere una passeggiata come no, dipende da quanto traduce e da quali sono le limitazioni che impone (perché sicuramente non compilerà ogni singola feature di OCaml, poiché alcune di esse non sono nemmeno esprimibili in js, se non riproducendole tramite un qualche pattern js in cui si perde probabilmente il fulcro stesso della feature).

Infine, vorrei pensare ad ultimo aspetto: la mantenibilità. Ora come ora Polygen è 1 programma standalone scritto in 1 linguaggio (OCaml). Se domani mattina lo cross-compiliamo in js, al netto del problema di refactoring di cui sopra, sarà necessario mantenere 2 distinte versioni del sorgente. Naturalmente potremmo dismettere totalmente la versione OCaml e tenere solamente quella js, ma questo significa che in futuro, qualunque siano gli interventi e gli upgrade al polygen, dovranno essere implementati (a) in javascript, (b) intervenendo in un sorgente che non è stato scritto da un umano e (c) usando un linguaggio pessimo e per niente adatto alla scrittura di software di genere term manipulation. Questi sono 3 grossi ostacoli che possono essere sormontati tutti contemporaneamente se invece prediamo un'altra strada: continuiamo a mantenere ed upgradare polygen in OCaml ed introduciamo la cross-compilazione nel processo di building. In altre parole: il programma (o libreria, o entrambi) rimane bello e pulito, scritto in OCaml, e la versione js la generiamo a build time ogni volta. Per poter imboccare questa strada però occorre sapere come funziona la cross compilazione ml-to-js. E quindi ritorniamo al punto di prima.

C'è anche un'altra alternativa, sebbene meno realistica: potrei riscrivere Polygen in Haskell e mantenerlo per sempre in Haskell in ogni sua forma, libreria o altro, appoggiandomi al nutrito kit di strumenti per il web di cui Haskell e la sua community oggi dispongono. Haskell è un linguaggio che non fa rimpiangere OCaml (anzi, il suo type system è ancora più potente) e gode anche di ottima salute, quanto a ecosistema e supporto. Anche Scala sarebbe un candidato con grossomodo le medesime proprietà di Haskell. Si tratta però di lavoro più profondo ed intenso di quel "consolidamento" che avevo/avevamo in mente: per quanto polygen sia già scritto, tradurlo a mano significa riscriverlo secondo le convenzioni, gli standard e gli stili imposti dal linguaggio target. E non possiamo scendere a compromessi di qualità, altrimenti viene meno l'obiettivo primario del porting, ovvero la mantenibilità. Sì, insomma, è un lavoraccio pure questo alla fine della festa, e non sono sicuro di aver voglia :P

Il giorno 16 gennaio 2018 15:22, Tristano Ajmone <notifications@github.com

ha scritto:

Sto spulciando i progetti OCaml menzionati nella tabella; alcuni sono veri e propri backend per il compilatore OCaml, altri sono virtual machine che consentono di far girare codice oggetto OCaml nel browser.

Sono davvero progetti fantastici, e sembra potrebbero essere in grado di compilare PolyGen senza ulteriori strumenti.

Javascript è un linguaggio davvero performante, ed è molto più universale che non il Java (a mio avviso). Una volta creata la libreria di PolyGen ( libPolyGen?), con una sua API, il resto sarebbe una passeggiata.

Le librerie statiche e dinamiche potrebbero essere incorporate in applicazioni di vario tipo, il che consentirebbe anche di implementare una nuova interfaccia da riga di comando in qualsiasi linguaggio (che so, per aggiungere nuove opzioni e funzionalità, filtri, ecc.).

La libreria in Javascript consentirebbe di utilizzare Polygen direttamente nel browser, buttando il carico di lavoro sul client e scavalcando la necessità di un server, interfacciamenti CGI, PHP, ecc. La pagina web si limiterebbe a fornire il sorgente javascript, ma siccome le risorse di calcolo passano al client, anche se 10.000 utenti in contemporanea continuassero a generare testo il server non consumerebbe alcuna risorsa.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/alvisespano/Polygen/issues/10#issuecomment-357974813, or mute the thread https://github.com/notifications/unsubscribe-auth/ACcZxg67rMzP-wWyTxLNnSgug-5z5IDUks5tLLCWgaJpZM4Rf0md .

tajmone commented 6 years ago

Di riscriverlo in javascript non ci penso neanche

scusa, devo essermi espresso male. Intendevo solo la cross compilazione di PolyGen come libreria accessible tramite API.

Il problema è che la cross-compilazione è una bestia strana: bisogna scoprire COSA cross-compila e cosa produce in output. Se è in grado di tradurre i binding a top level di un modulo ML in binding a top level js allora direi che è meglio prima scrivere la libreria in OCaml (così il refactoring ed il testing è fatto in linguaggio migliore) e poi la tradurla;

Da quel che ho visto vi sono vari approcci e opzioni in merito. Credo che la libreria OCaml sia comunque la strada migliore, e forse la più facile. Purtoppo OCaml fa parte di una categoria di linguaggi che non conosco, e non posso esserti d'aiuto, se no mi ci sarei buttato. Presumo che, come nella maggior parte dei linguagi high level, si tratti solo di separare il codice che interfaccia alla riga di comando (opzioni, e varie) dal "motore" vero e proprio (lexer, parser, etc) così da rendere accessibili le funzioni di quest'ultimo tramite una API pubblica.

Infine, vorrei pensare ad ultimo aspetto: la mantenibilità ... se invece prediamo un'altra strada: continuiamo a mantenere ed upgradare polygen in OCaml ed introduciamo la cross-compilazione nel processo di building. In altre parole: il programma (o libreria, o entrambi) rimane bello e pulito, scritto in OCaml, e la versione js la generiamo a build time ogni volta.

Concordo, questa strada è l'unica che ha senso (e che avevo in mente). Inoltre, il gioco vale la candela solo questa cross compilazione è davvero un processo che si riduce a creare un makefile e che di tutto il resto si occupino i tool delegati al compito (se così non fosse, la mantenibilità andrebber a farsi friggere).

Per poter imboccare questa strada però occorre sapere come funziona la cross compilazione ml-to-js. E quindi ritorniamo al punto di prima.

Su questo posso documentarmi, ma non conscendo OCaml al primo intoppo entrerò nel panico. Comunque, posso installare il tutto su Linux e sperimentare. Di sicuro la docuemntazione non manca per questi progetti, che sono poi solo tre dato che uno dei quattro linkati è fermo da otto anni.

C'è anche un'altra alternativa, sebbene meno realistica: potrei riscrivere Polygen in Haskell

Haskell sta andando alla grande. Ma vale la pena riscrivere ciò che già funziona bene? Voglio dire, ok OCaml è un po' stagnante come linguaggio al momento, ma ho visto che è comunque mantenuto attivamente in alcune sue versioni, e su Linux sarà comunque disponibile senza problemi.

Si tratta però di lavoro più profondo ed intenso di quel "consolidamento" che avevo/avevamo in mente: per quanto polygen sia già scritto, tradurlo a mano significa riscriverlo secondo le convenzioni, gli standard e gli stili imposti dal linguaggio target. E non possiamo scendere a compromessi di qualità, altrimenti viene meno l'obiettivo primario del porting, ovvero la mantenibilità. Sì, insomma, è un lavoraccio pure questo alla fine della festa, e non sono sicuro di aver voglia :P

Direi che è uno sbattone troppo grosso. Un conto è se tu avessi voglia di scrivere una nuova versione da zero, finalizzata al supporto Unicode e altre cose — fregandotene della retrocompatibiltà con le gramamtiche attuali. Ma se è solo per far girare le grammatich odierne, non ne vale la pena. In fondo si trova sempre il modo di farlo funzionare anche su Windows (che su Linux il problema neanche si pone). Secondo me anche fra 10 anni lo si potrà eseguire su un PC, d'altronde esistono emulatori MS-DOS, ZX-81, C-64, e via dicendo.

Però, se la cross compilazione fosse davvero roba da un click e vai ... allora sarebbe una manna dal cielo. E, poter far girare PolyGen nel browser sarebbe fico, e non credo che l'utente medio (con 8 o più Core a 64 bit) noterebbe problemi o differenze di performance.

javascript è anche il peggior linguaggio della storia dell'umanità ... un linguaggio unsound.

Oltre ad essere brutto javascript è anche lento, non è vero che è performante se paragonato ai linguaggi con un compilatore di qualità (come C per esempio, o lo stesso OCaml).

Allo stato attuale, un sorgente Java o C# cross-compilato in Javascript girà a metà delle velocità rispetto a programma nel linguaggio originale. Direi che non è male. Però paragonare il linguaggio di scripting a uno studiato per la compilazione binaria non ha molto senso. Python è 13 volte più lento di un programma in C compilato, ma è normale che sia così dato che è un programma di scripting no?

Tuttavia - forse tu intendi - è performante in senso relativo, cioè, per essere un pastrocchio interpretato che gira su un browser, javascript non è male;

Io non uso Javascript nel browser, lo uso sul PC tramite Node.js, quindi senza limiti di sand-boxing. La cattiva reputazione di Javascript è dovuta al fatto che molti l'associano alla sandbox Javascript dei browser (e all'orribile periodo delle "guerre dei browser" e le scadenti implementazioni di Javascript).

Fuori dal browser, Javascript è performante per via delle funzioni asincrone. Chiaramente, come ogni linguaggio, vi sono settori in cui spicca di più e quelli in cui spicca di meno. Però un pastrocchio?

alvisespano commented 6 years ago

Quando dico che Javascript è un brutto linguaggio non intendo dire che non sia utile o usabile - ogni giorno lo usano milioni di persone e mezzo web è scritto in javascript. Quello che intendo è questo: ci sono linguaggi inventati da linguaggisti, con l'avvallo della comunità scientifica, che implementano o inventano feature avanzate studiate in letteratura; altri invece non sono così. Non è una forma di snobismo: la scienza dei linguaggi esiste, come esista la fisica delle particelle o la chimica organica, e ha raggiunto dei traguardi nel corso dei decenni. I linguaggi che sono figli di questa scienza hanno delle proprietà matematiche importanti, come ad esempio la soundness, che garantisce che un programma che compila allora funziona per forza - nel senso che non può fallire a runtime, non nel senso che fa quello che il programmatore aveva in mente. Tutti gli ML sono di questa categoria: sono linguaggi "d'autore", nel senso che sono fatti da scienziati (informatici) che hanno dedicato la vita allo studio ed all'approfondimento della materia. Haskell è il top in questo: creato a mantenuto sostanzialmente dalla comunità scientifica dei linguaggi. Idem per OCaml, Scala, Clojure ed altri (anche Lw, che ho creato io nel mio piccolo, appartiene a questa famiglia).

E poi ci sono i linguaggi cosiddetti industriali: C, C++, Java, C#, PHP, Python, Javascript, Go, Rush, Ruby e moltissimi altri, impossibili da elencare tutti. Sono linguaggi più semplici, fatti da persone in gamba naturalmente, ma non sono conosciuti per le loro feature ground-breaking o per aver portato avanti il carretto della scienza dei linguaggi. Sono linguaggi pratici, utili, usatissimi - che io stesso conosco molto bene e che uso regolarmente - ma non sono "il top possibile ad oggi" perché ignorano tutta una serie di traguardi formali raggiunti in letteratura e propongono feature arcinote.

Non c'è nessuna forma di aristocrazia in ciò che dico: è un dato di fatto riconosciuto da chi si occupa di linguaggi. Rispetto moltissimo l'autore di Python, per esempio, ma Python è un linguaggio che non ha inventato niente - solamente fa bene cose note da 40 anni. Ci sono dei meriti anche in questo, non fraintendermi! Ma OCaml o Haskell sono tutto un altro livello: è come paragonare una Lamborghini con una Golf.

Fatta questa doverosa premessa, Javascript è un linguaggetto creato da "mestieranti", non da esperti di linguaggi, che è diventato standard globale suo malgrado. Spesso accade che diventino mainstream cose che non dovrebbero diventarlo, dal punto di vista qualitativo. In particolare Javascript è un pasticcio di linguaggio: è totalmente unsound ed è molto facile scrivere programmi che non funzionano perché le scelte di design sono orientate allo scrivere poco ed al deploy facile, non alla robustezza, alla sicurezza del codice ed al riuso.

iacopy commented 6 years ago

E vai di Haskell allora :)

pdonadeo commented 3 years ago

Forse potreste essere interessati al mio fork di Polygen: gira nel browser come libreria che, per ora, espone solo una funzione di generazione.

https://github.com/pdonadeo/Polygen

tajmone commented 3 years ago

@pdonadeo:

Forse potreste essere interessati al mio fork di Polygen: gira nel browser come libreria che, per ora, espone solo una funzione di generazione.

Grazie del link! Cercherò di aggiornare i vari Wiki per linkarlo, appena trovo il tempo.

alvisespano commented 3 years ago

Grazie! Molto carino! Funziona come una unica funzione con una grammatica come unico argomento? Ricordo che tanti anni fa l'aveva fatta anche Zeff (l'amico che fece il sito polygen.org originale) tipo nel 2003, poi nel 2007, poi nel 2010 e di nuovo nel 2011, finché si è rotto i coglioni perché ogni anno cambiavano le tecnologie web e bisognava rifare il wrapper da capo :D Cmq apprezzo molto! Me ne intendo poco, ma credo che oggi le tecnologie web siano un pochino più stabili e di vita semi-lunga, speriamo :)

pdonadeo commented 3 years ago

Sì è una unica funzione, esportata come Polygen.generate che ha come unico argomento una stringa con la grammatica.

Se interessa posso scrivere un minimo di documentazione su come compilare il progetto con un ambiente OCaml moderno. Penso che Polygen nel browser apra infinite possibilità :D

Questa è una micro demo.

https://user-images.githubusercontent.com/242049/107932981-771ec600-6f7e-11eb-8c86-c8ccdeb1f4d5.mp4

alvisespano commented 3 years ago

Assolutamente Paolo! E' carinissimo e apre la porta, come dici tu, a infinite possibilità. Se hai voglia di scrivere un po' di doc, magari con la consulenza di @tajmone, sarebbe perfetto. Potremmo anche pensare di integrare il tuo wrapper web al polygen ufficiale, per esempio linkandolo ufficialmente e/o menzionandolo.

tajmone commented 3 years ago

Se hai voglia di scrivere un po' di doc, magari con la consulenza di @tajmone, sarebbe perfetto.

Concordo, la documentazione non è mai abbastanza né di intralcio, e sono disponibile.

Potremmo anche pensare di integrare il tuo wrapper web al polygen ufficiale, per esempio linkandolo ufficialmente e/o menzionandolo.

Non ho avuto modo di sbirciare ai sorgenti, quindi volevo capire: si tratta di una cross-compilazione da OCaml a JavaScript (WebASM, o simili)? In quel caso, si potrebbe pensare di aggiungerlo alla toolchain ufficiale, così le due applicazioni si aggiornano di pari passo.

Ma, anche se non fosse un caso di cross-compilazione, si potrebbe pensare di integrare la demo HTML (e quanto serve) nel repository ufficiale stesso, come esempio di utilizzo (specie, appunto, se buttiamo giù della documentazione).

Tra gli ovvi vantaggi, l'automazione dei test tramite servizi CI (GitHub Actions, Travis CI, Cilcle CI, o quant'altro) che potremmo eseguire a nostra discrezione ad ogni commit/PR, o prima di creare una nuova tagged release.

pdonadeo commented 3 years ago

Non ho avuto modo di sbirciare ai sorgenti, quindi volevo capire: si tratta di una cross-compilazione da OCaml a JavaScript (WebASM, o simili)? In quel caso, si potrebbe pensare di aggiungerlo alla toolchain ufficiale, così le due applicazioni si aggiornano di pari passo.

Si tratta di una cross compilazione ma i sorgenti non sono identici perché l'ultima release non compilava con la toolchain "ufficiale" OCaml, che non è basata su make ma usa dune.

Però concordo al 100% che sarebbe interessante includere tutto nello stesso repository. Ma dune è incompatibile con make nel senso che mette tutti gli artefatti (cm*) nella directory _build che gestisce per i fatti suoi. Il problema è che se trova degli artefatti fuori da _build si arrabbia e va in errore.

Ad ogni modo potrei:

  1. eliminare il makefile e gli altri file della toolchain classica;
  2. aggiungere un nuovo "main" solo per Javascript: deve essere per forza diverso dal main di Polygen perché questo è un programma, con una sua command line, che esegue una generazione mentre quella Javascript è una libreria che espone delle funzioni (in questo momento solo una), ma non fa niente. Il vero "main" lo deve scrivere il programmatore Javascript. A questo proposito La Cosa Giusta Da Fare ©® è estrarre da main tutto quello che si può fattorizzare per tenerlo identico tra programma e libreria e minimizzare le differenze.
  3. impostare la toolchain con dune che a quel punto può essere "istruito" per compilare sia il programma nativo sia la libreria JS.

Se questa strada vi sembra quella giusta nel fine settimana ci lavoro: cancello il mio repo attuale, che è un po' un pasticcio, faccio un nuovo fork, includo queste modifiche e faccio una regolare pull request.

pdonadeo commented 3 years ago

Dimenticavo un "piccolo dettaglio": non ho la più pallida idea di come compilare Polygen sotto Windows né di come compilare qualsiasi cosa sotto Windows. Non uso Windows da tipo 20 anni. Io inizio con Linux, poi vediamo.

lapo-luchini commented 3 years ago

Se poi vogliamo aggiornare il sito per togliere la chiamata al binario* e lasciarla fare direttamente al client, fatemi un fischio. 😇

*: l'ho messo in una jail e non ho bisogno di compilarlo da quando il sito è su, ma se si può evitare una chiamata ad un binario da parte di PHP (che tra l'altro sembra non girare con la 7.0 e ho dovuto lasciare per ora alla 5.6), potrebbe essere una buona idea a prescindere… (o forse no: squadra vincente non si cambia?)

tajmone commented 3 years ago

@pdonadeo:

Si tratta di una cross compilazione ma i sorgenti non sono identici perché l'ultima release non compilava con la toolchain "ufficiale" OCaml, che non è basata su make ma usa dune.

Però concordo al 100% che sarebbe interessante includere tutto nello stesso repository. Ma dune è incompatibile con make nel senso che mette tutti gli artefatti (cm*) nella directory _build che gestisce per i fatti suoi. Il problema è che se trova degli artefatti fuori da _build si arrabbia e va in errore.

Se ci fosse un modo per far convivere le due build nello stesso repository sarebbe l'ideale.

Da una rapidissima scorsa, mi pare di capire che Dune potrebbe tranquillamente sostituire Make nel repository ufficiale, ed essere utilizzato per compilare sia i binari "classici" che per la cross-compilazione a JavaScript.

Ti risulta possibile farlo @pdonadeo?

Inoltre, sembra offrire funzionalità superiori a Make (non che io sia un fan di Make, anzi...), e sul sito ufficiale leggo inoltre:

About 40% of OPAM packages are built using Dune.

Ovviamente, sta ad Alvise decidere, ma secondo me sarebbe una buona mossa raggruppare più sottoprogetti Polygen possibili in un solo repository, anziché disperderli e diluirli. In fondo, la comunità di sviluppo di Polygen non è enorme, quindi ha senso unire gli sforzi. In questo caso specifico, dato che la base del codice condivisa è la medesima, ha ancora più senso e consentirebbe di unire gli sforzi manutentivi sotto un unico tetto.

  • aggiungere un nuovo "main" solo per Javascript: deve essere per forza diverso dal main di Polygen perché questo è un programma, con una sua command line, che esegue una generazione mentre quella Javascript è una libreria che espone delle funzioni (in questo momento solo una), ma non fa niente. Il vero "main" lo deve scrivere il programmatore Javascript. A questo proposito La Cosa Giusta Da Fare ©® è estrarre da main tutto quello che si può fattorizzare per tenerlo identico tra programma e libreria e minimizzare le differenze.

Questa operazione sarebbe utile in generale, non solo per la version JavaScript, dato che consentirebbe di usare Polygen come libreria (statica o dinamica) direttamente da altre applicazioni. Se si riuscisse ad aggiungere alle build la creazione di una libreria dinamica, risulterebbe estremamente facile integrarla in altri programmi, a prescindere dal linguaggio in cui sono scritti — specie su Windows, dove risulta particolarmente rognoso integrare librerie statiche creati in altri linguaggi, a causa dei disastri storici dei compilatori C della MS, incompatibilità delle ABI nel corso del tempo, ecc.

Ma quest'ultima operazione potrebbe richiedere un bel po' di lavoro di refactoring, e io non sono in grado di aiutare molto dato che non conosco OCaml, e quando venne creato il repository non riuscì neanche a trovare una versione del compilatore OCaml richiesta che funzionasse su Win 10.

@alvisespano, che ne pensi di queste proposte?

Da quando abbiamo creato i vari repository Polygen su GitHub qualche cosa si è mossa. Abbiamo ripubblicato e completato la documentazione ufficiale, tu hai ripulito i sorgenti per renderli utilizzabili con l'ultima versione OCaml, e non sono mancati vari progetti open source legati a Polygen.

Sono tutti segnali positivi che indicano che l'interesse per Polygen è ancora vivo. La creazione di una "libPolygen" sarebbe un ottimo passo avanti per l'integrazione di Polygen in vari applicativi, oltre che per poter cross-compilare la versione JavaScript.

pdonadeo commented 3 years ago

Se ci fosse un modo per far convivere le due build nello stesso repository sarebbe l'ideale.

Temo sia impossibile. Ad ogni modo nel mio fork c'è già il nuovo build system che compila, per ora, solo il classico polygen e lo installa.

Step:

  1. installare opam: https://opam.ocaml.org/doc/Install.html
  2. clonare il mio repo: git clone --recursive git@github.com:pdonadeo/Polygen.git
  3. $ cd Polygen; opam install .

Questo funziona certamente sotto Linux con un ambiente "standard" di OCaml, ovvero installato con opam. Potrei scommettere che funziona anche su OSX senza nessuna modifica. Sotto Windows, come detto, non ne ho la minima idea :sweat_smile:

Per quanto riguarda l'estrarre una libreria mi sembra un'ottima idea ma qualcuno (Alvise?) mi deve dare una mano perché non ho la minima idea di quali funzioni esporre.

tajmone commented 3 years ago

Se ci fosse un modo per far convivere le due build nello stesso repository sarebbe l'ideale.

Temo sia impossibile. Ad ogni modo nel mio fork c'è già il nuovo build system che compila, per ora, solo il classico polygen e lo installa.

Anche qualora il core di Polygen fosse spostato in un modulo independente? Nella maggior parte dei linguaggi questa operazione è fattibile, non capisco perché OCaml o i suoi strumenti dovrebbero impedirlo (ma, come già detto, non conosco OCaml).

Questo funziona certamente sotto Linux con un ambiente "standard" di OCaml, ovvero installato con opam. Potrei scommettere che funziona anche su OSX senza nessuna modifica. Sotto Windows, come detto, non ne ho la minima idea

Stando alla documentazione ufficiale di dune, il supporto cross-platform dovrebbe essere semplificato da dune stesso:

cross-platform: as long as your code is portable, dune will be able to cross-compile it (note that dune is designed internally to make this easy but the actual support is not implemented yet)

Però, visti tutti i problemi che riscontrammo all'epoca per imbastire una toolchain OCaml che funzionasse su Windows, non ci farei affidamento. Ad ogni modo, su Windows 10 si può sempre ripiegare su WSL che consente di eseguire un'istanza del Kernel Linux in sottofondo, e quindi cross-compilare per Windows mischiando strumenti puramente Linux e applicativi Windows (grazie al bridge tra Windows a WSL). Se ben ricordo, all'epoca dovemmo ricorrere a WSL per alcuni applicativi OCaml richiesti dagli editor (ma allora WSL era ancora agli albori, e pieno di bachi, mentre oggi WSL2 dispone di un vero e proprio Kernel Linux).


PS: @pdonadeo, ho aggiunto il link al tuo repository in entrambi i Wiki Polygen:

se dovessi mai chiudere il repository segnalamelo, o magari aggiorna le pagine dei Wiki tu stesso (dovrebbero essere editabili da chiunque).

pdonadeo commented 3 years ago

Credo di essere ad un punto stabile. Quello che ho fatto è questo:

  1. prendere l'ultima versione stabile (1.0.6) di Polygen;
  2. disaccoppiare la libreria dall'eseguibile: la libreria Polygen_lib non è altro che l'insieme di tutti i moduli presenti (Prelude, Absyn, Check, eccetera) più 3 funzioni che ho estratto perché sono usate dal programma polygen. Ho dovuto fare qualche piccolissima modifica qua e là per sostituire le funzioni deprecate ed eliminare qualche warning, ma nulla di che;
  3. Esportare in Javascript 2 funzioni. Sono: pRNG_init (esportata come pRngInit) e generate (esportata con lo stesso nome). Tutto il resto della Polygen_lib non è accessibile in questo momento da Javascript, la cosa richiederebbe un lavoro lungo e noioso che non farò, ve lo dico subito.

L'effetto netto è questo: compilando ed installando il mio fork uno si trova installato il caro vecchio polygen, il programma, identico a com'era. Inoltre può scrivere un altro programma usando Polygen_lib.

Caricando il file .js ci si trova nel browser un oggetto Polygen con dentro la funzione per inizializzare la sequenza casuale e generate che ha due parametri: una grammatica ed un oggetto di configurazione contenente start e lbs, eventualmente vuoti.

const grammatica = "...";
let frase = Polygen.generate(grammatica, {});

Dentro frase c'è una stringa JS che può essere infilata nel Dom.

Se avete voglia di provare e vedere se vi piace queste sono le istruzioni:

  1. installare opam: questo è importante, opam è un componente imprescindibile nell'ecosistema OCaml di oggi
  2. git clone --recursive https://github.com/pdonadeo/Polygen.git
  3. cd Polygen/
  4. opam switch create ./ 4.11.2

L'ultimo step crea uno switch locale di opam, installa la versione 4.11.2 del compilatore, tutte le dipendeze di polygen e polygen stesso. Dopo che ha finito dovreste poter scrivere polygen ed ottenere:

$ polygen
Polygen (type 2) v1.0.6 build 20180122 - http://www.polygen.org
Manta/Spinning Kids alias Alvise Spano' anno MMII ac insequenti fecit.

usage: polygen [OPTION]... SOURCES...

 SOURCE     source file(s) providing grammar definition

 OPTION
  -eof STR  use string STR as end-of-file (default: STR = "\n")
  -help     display this help message
  -info     alias for '-S I'
  -l LABEL  add LABEL to initial active label environment
  -o DEST   output to DEST file
  -pedantic set warning level to maximum
  -pre      output preprocessed source grammar
  -seed N   pass unsigned integer N as random seed
  -S SYM    use SYM as starting non-terminal symbol (default: SYM = S)
  -t        check source grammar and output inferred global label groups
  -v        be verbose
  -X N      iterate generation for N times (default: N = 1)
  -W N      set warning pedantry at level N (default: N = 1)
  --help  Display this list of options

Se vi piace modifico il README con le istruzioni e faccio pull request.

tassoman commented 2 years ago

Non s'è mergiata poi questa issue? :cry:

pdonadeo commented 2 years ago

Non s'è mergiata poi questa issue? cry

Pare di no, e c'è pure una pull request.

tajmone commented 2 years ago

Non s'è mergiata poi questa issue?

@tassoman, questo Issue conteneva solo una proposta per un'eventuale transpilazione da OCaml a JS, ma nessun codice o implementazione di fatto.

Benché ritengo che la cosa sia fattibile, richiederebbe comunque parecchio lavoro in termini di ritrocchi al codice sorgente e alla toolchain (e un'approfondita conoscenza di OCaml, linguaggio che personalmente non conosco affatto).

pdonadeo commented 2 years ago

@tajmone scusa ma ti sbagli: la patch è un porting completo di Polygen all'interno di un ecosistema relativamente recente di OCaml (4.11.2) con build system aggiornato, modifiche a tutti i sorgenti per adattamento alle nuove interfacce, eccetera.

E in più c'è la transpilazione in Javascript, perfettamente funzionante.

tassoman commented 2 years ago

Stavo pensando che forse, anziché andare a stravolgere tutto, si potrebbe comporre un micro servizio in un container che fa la stessa cosa (polygen.generate) tramite chiamata asincrona via http.

Stamattina ho scritto qualcosa, per ora è un obbrobrio... ma funziona. Il «trucco» sarebbe comporre da l'immagine ubuntu, che contiene il poly 1.0.6 nel repository ufficiale, senza stare a compilare da zero. Di conseguenza servire tramite Python, che manda una chiamata di sistema al binario e restituisce l'output tramite apiflask

Però le due cose non si escludono, la libreria js potrebbe essere integrata direttamente nei progetti tramite node

tajmone commented 2 years ago

@tajmone scusa ma ti sbagli: la patch

Mi riferivo al fatto che si menzionava il merge di questo Issue:

Non s'è mergiata poi questa issue?

Questo Issue non può essere mergiato poiché non è un pull-request né contiene modifiche al codice.

Credo che la patch in questione sia il pull-request #22, e che quindi convenga sollecitare nei commenti della PR anziché qui per quanto riguarda la patch (autori diversi per l'uno e per l'altra) .

pdonadeo commented 2 years ago

Stavo pensando che forse, anziché andare a stravolgere tutto, si potrebbe comporre un micro servizio in un container che fa la stessa cosa (polygen.generate) tramite chiamata asincrona via http.

Comunque sia la PR #22 non "sconvolge" niente: Polygen — allo stato attuale di questo repository — non compila nemmeno.

tajmone commented 2 years ago

Comunque sia la PR #22 non "sconvolge" niente: Polygen — allo stato attuale di questo repository — non compila nemmeno.

Sì, è un peccato che il progetto sia in stallo, e che la tua PR non sia stata integrata (un sacco di lavoro rimasto in sospeso).

Credo che allo stato attuale la cosa migliore sia spostare la discussione sulla PR #22 al fine di spronarne il merge — anche se Polygen non compila, la tua variante in JS compila, quindi integrare la PR mi sembra la cosa sensata.

Non ho la minima idea del perché quella PR non sia stata ancora integrata in oltre un anno, ma non risultano conflitti né impedimenti.