Ada naar JavaScript vertalen met A2JS
          =====================================
                        2020-11-23


Als webprogrammeur heb ik aan de voorkant vaak te maken  met
JavaScript.  Logisch, want het  is  de  enige  "high  level"
programmeertaal  die  zit   ingebakken   in   elke   moderne
webbrowser.  Dit zou  niet  erg  zijn,  ware  het  niet  dat
JavaScript als taal wat problematisch is.  Toen  hij  in  de
jaren negentig  werd  ontwikkeld  was  dit  een  beetje  een
haastklus en de gevolgen daarvan zijn nog  steeds  terug  te
vinden.  Om dit probleem te ondervangen  zijn  er  een  hoop
manieren uitgevonden om  andere  talen  naar  JavaScript  te
compileren (of  eigenlijk  transpileren  :-).   Het  is  ook
mogelijk om Ada naar JavaScript te  compileren  door  middel
van het A2JS gereedschap dat onderdeel is van het  Matreshka
project [3].

Moderne webapplicaties worden steeds complexer. Programmeurs
hebben daarbij  de  voorkeur  om  zoveel  mogelijk  werk  te
verrichten in de webbrowser zelf.  Waar voorheen  de  server
simpelweg een HTML-pagina genereerde,  is  het  nu  vaak  de
browser die aan de hand van  vele  honderden  kilobytes  aan
scripts dynamischerwijs het scherm opbouwt.  De  voornaamste
taal om al dit browserwerk mee te verrichten is  JavaScript.
JS is een dynamische programmeertaal die is  opgebouwd  rond
de de ECMAScript specificatie.  JS is een  vrij  ongedwongen
taaltje.  Je hoeft geen types aan te  geven,  er  wordt  ook
geen  programmeerparadigma  afgedwongen  en   er   is   geen
standaard modulesysteem.  De prijs  voor  al  deze  vrijheid
betaal je in de valuta van programmeerdiscipline en  dat  is
ook gelijk de kern van het probleem met deze taal.  Er staat
je weinig in de  weg  om  hele  geniepig  foutieve  code  te
schrijven, dus moet je heel secuur  werken  als  je  grotere
JS-projecten tot een  goed  eind  wil  brengen.   Dit  wordt
tegenwoordig  wel   gemakkelijker   met   allerhande   hippe
statische analysetools als Flow en typechecks  aan  de  hand
van JSDoc en Typescript, maar toch voelt  het  allemaal  wat
verminderd ergonomisch  als  je  zorgeloos  wil  doorwerken.

Om het leven als webprogrammeur  wat  draaglijker  te  maken
zijn er in de loop der jaren een hoop talen uitgevonden  die
gecompileerd kunnen worden naar JS.  Dit stelt de  coder  in
staat om in een andere omgeving haar werk te doen, maar toch
hippe dynamische webapps te bouwen.  Ik heb  veel  van  deze
talen gebruikt en o.a  projecten  gebouwd  in  CoffeeScript,
TypeScript, Dart, PureScript en Fay.  Al deze  talen  bieden
een manier om de intrinsieke zwaktes van JS  te  compenseren
en welke taal beter dan alle andere om zwaktes intrinsiek of
extrensiek  te  overwinnen  dan   Ada   de   verkwikkelijke?

De mensen van het  Matreshka  project  zijn  zo  vriendelijk
geweest  om  een  eigen  Ada  naar  Javascript  compiler  te
ontwikkelen: A2JS. Ze maken hiervoor gebruik van ASIS en dat
is erg slim, want daardoor kunnen ze volledig leunen  op  de
krachtige GNAT compiler om het zware werk te doen.  ASIS  is
een systeempje dat inhaakt op GNAT en allerhande  informatie
rapporteert over  Ada-code.   Op  deze  manier  kun  je  een
complete syntaxboom genereren en via die route gebruiken  in
allerhande statische analysetools of in het geval  van  A2JS
in een vertaler.

De online documentatie over A2JS is een tikje sumier [4]  en
het blijkt dat A2JS niet zozeer een losse tool is,  als  wel
een onderdeel van het complete Matreshka project.  Matreshka
kun je gebruiken als framework voor Ada-applicaties en lijkt
zich voor een groot deel te richten op  webapplicaties.   De
beste manier om te kijken hoe Matreshka werkt is dus  om  de
code te downloaden  en  alles  handmatig  door  te  ploegen.

Matreshka is vrij netjes opgebouwd en de code voor  A2JS  is
redelijk separaat.  Er zit gelukkig een test  suite  bij  en
door die te bekijken kon ik snel zien  hoe  je  A2JS  in  de
praktijk kunt gebruiken. Ik heb een eenvoudige "hello world"
webapplicatie gemaakt en de code daarvan vind  je  [op  mijn
Sourcehut [1].

Om  A2JS  te  compileren  moet   je   eerst   een   werkende
ASIS-installatie hebben.  ASIS is een tikje gevoelig,  omdat
hij qua versie precies overeen moet komen met je installatie
van GNAT. De gemakkelijkste manier om beide versies te laten
matchen is door ze gezamenlijk te installeren,  bijvoorbeeld
vanuit de repository van je Linux-distributie.  Ze  los  bij
elkaar zoeken is wat meer werk en  leidt  niet  per  se  tot
verhoogde levensvreugde.

Zodra je A2JS goed geïnstalleerd hebt kun je JavaScript  als
compileerdoel selecteren in je gpr-bestand:

with "matreshka_league";

project Test is
  Work_Dir := external ("WORK_DIR");
  for Target use "javascript";

  for Object_Dir use Work_Dir & "/.objs";
  for Source_Dirs use ("src");
end Test;

Zolang je je in je  project  houdt  aan  de  Ada-subset  die
ondersteund wordt door A2JS zal hij  bij  het  compilen  een
hele verzameling js-bestanden aanmaken.  Elk onderdeel wordt
zijn eigen bestand en via RequireJS kan alles  in  samenhang
worden  aangeroepen  in  een   Node-   of   browseromgeving.

Dit alles werkt eigenlijk vrij goed.  GNAT is een  geweldige
compiler en eventuele Ada-gerelateerde issues zal hij  zoals
gewoonlijk oppikken.  A2JS maakt weinig rare sprongen om  je
code naar JavaScript om te zetten en de  manier  waarop  het
dit doet  is  vrij  goed  te  volgen  in  de  broncode.   De
verschillende taalonderdelen van Ada zijn  allemaal  in  hun
eigen bestand ondergebracht  en  de  vertalingen  zijn  vrij
eenvoudig.

Hier heb je bijvoorbeeld de inhoud van de  functie  die  een
while-statement omzet naar JS:

Text.Append ("while (");
Down := Engine.Text.Get_Property (Cond, Name);
Text.Append (Down);
Text.Append ("){");

Down := Engine.Text.Get_Property
(List  => List,
Name  => Name,
Empty => League.Strings.Empty_Universal_String,
Sum   => Properties.Tools.Join'Access);

Text.Append (Down);

Text.Append ("};");

return Text;

De combinatie van de ASIS-statements en de no-nonsense wijze
waarop de syntaxboom wordt  bewandeld  maakt  A2JS  tot  een
fraai stuk gereedschap en gegeven de functionaliteit  is  de
ruime negenduizend regels code niet al te veel.  Zeker  niet
als je meerekent dat het gros  van  de  regels  bestaat  uit
veelvoorkomende boombewandelmantra's.

De mensen van Matreshka zijn zo vriendelijk geweest om naast
een eigen runtime library ook een WebAPI aan te bieden. Deze
kun je gebruiken voor  onder  andere  Ajax-calls,  WebGL  en
allerhande DOM-manipulatie. Dat is vriendelijk van ze, zeker
omdat je voor overige interoperabiliteit met het  JavaScript
universum bent aangewezen op de Foreign  Function  Interface
en die wil wel wat toetsenbordslijtage ten  gevolge  hebben.

Al met al is A2JS een aardig  project.   GNAT  is  een  hele
krachtige compiler en dat werkt sowieso lekker. Daarnaast is
de opbouw van A2JS mooi overzichtelijk.  Het is wel  spijtig
dat er vrijwel geen online documentatie  beschikbaar  is  en
dat het volledig is ingebed in het  Matreshka  project.   Ik
vermoed dat het  succesvoller  zou  zijn  als  het  als  los
gereedschap verkrijgbaar was.

Desalniettemin denk ik  dat  ik  A2JS  zelf  niet  snel  als
JavaScript vervanger zal inzetten.  Er zijn namelijk wel een
aantal belangrijke nadelen:

- A2JS genereert geen source maps.  Als je dus  een  runtime
 error hebt dan weet je niet welke regel in je Ada-code  de
 boosdoener was.  Bij andere talen zoals  TypeScript  werkt
 dit heel prettig, maar hier moet je het zonder stellen. Nu
 moet ik zeggen dat ik jarenlang naar tevredenheid Fay  heb
 gebruikt en dat deze ook geen source maps aanmaakte,  maar
 daar kon  je  tenminste  in  1  oogopslag  zien  waar  het
 probleem zat door naar de gegenereerde JS te kijken.  Hier
 is dat iets lastiger.

- A2JS ondersteunt alleen een Ada-subset en doordat  je  het
 ook  zonder  de  uitgebreide   standaardbibliotheek   moet
 stellen wordt het allemaal wel wat karig.  Bovendien  moet
 je er in de praktijk achter komen wat wel en  niet  werkt.

- Ik ben zelf niet  zo'n  fan  van  de  afhankelijkheid  van
 RequireJS.  Ik begrijp dat ze een keus  moesten  maken  om
 modulariteit van de Ada packages in stand te houden in  de
 JavaScript-wereld, maar had hier liever gezien dat ze  dit
 aan de hand van de ouderwetse direct uitvoerende  anonieme
 functie hadden gedaan.  Nog liever  zou  ik  ondersteuning
 voor ECMAScript 6 zien bij het genereren van de JS met  de
 ingebouwde ondersteuning voor modules. Als ik tegenwoordig
 een vanille JS project moet bouwen, dan gebruik ik ES6  in
 combinatie met Rollup om de modules te bundelen  en  Babel
 om de boel te vertalen voor oudere browsers.  Ik  zou  het
 zelf mooier vinden als je wat  meer  vrijheid  had  in  de
 keuzes hierin, want je bent vaak niet de enige  speler  in
 een web-project en misschien afhankelijk van de voorkeuren
 van anderen.

Door de manier waarop A2JS is opgebouwd leent het zich  goed
voor toekomstige aanpassing en uitbreiding.  Ik  ben  daarom
ook benieuwd wat de mensen van het Matreshka  Project  ermee
gaan doen.  Het is eigenlijk zonde dat ze  het  nu  relatief
diep hebben weggestopt op hun website, want A2JS  heeft  ook
onafhankelijk van  het  overkoepelende  framework  potentie.

Ik ga voor nu in elk geval verder met de route  die  ik  een
tijdje geleden  had  uitgezet.   Ik  ontwikkel  weer  zoveel
mogelijk aan de serverkant en daar  waar  nodig  gebruik  ik
vanille JavaScript  met  een  hele  batterij  aan  statische
analysetools   om   de   chaos   op   afstand   te   houden.

PS:  en  WebAssembly  dan?   Naast  het  transpileren  biedt
WebAssembly nog een mogelijke route om andere  talen  in  de
browser te gebruiken.  Deze is echter meer  gericht  op  het
ombouwen van complete applicaties en  minder  geschikt  voor
interactie met de onderliggende API's van de  browser  zelf.
Er bestaat voor Ada  een  experimentele  GNAT-LLVM  compiler
waarmee je compileren [5], maar deze is bepaald  niet  klaar
voor productie.


Hyperlinks:
[1]: https://git.sr.ht/~jelle/a2js_demo
[2]: http://gnat-asis.sourceforge.net/
[3]: https://forge.ada-ru.org/matreshka
[4]: https://forge.ada-ru.org/matreshka/wiki/Web/A2JS
[5]: https://blog.adacore.com/use-of-gnat-llvm-to-translate-ada-applications-to-webassembly


-----------------------------------------------------------
                  Tags: ada, nederlands