POSTURI ANTERIOARE

reducerea

Slăbirea limbilor prin reducerea zahărului

Postat pe 08 ianuarie 2016.

JavaScript este un limbaj nebunesc. Este definit de 250 de pagini de proză engleză și chiar părțile limbii care ar trebui să fie simple, cum ar fi adăugarea și domeniul variabil, sunt foarte complicate. Am arătat înainte cum să abordăm această problemă folosind λs5, care este un exemplu al ceea ce se numește o semantică testată.

Puteți citi despre λs5 la linkul de mai sus. Dar ideea de bază este că λs5 are două părți:

  • Un limbaj de bază mic care surprinde părțile esențiale ale JavaScript, fără toate punctele sale slabe și
  • O funcție de desugaring care traduce limba completă până la acest nucleu mic.

(În mod obișnuit, acest limbaj de bază îl numim λs5, chiar dacă din punct de vedere tehnic este doar o parte din ceea ce alcătuiește λs5.)

Aceste două componente împreună ne oferă o implementare a JavaScript-ului: pentru a rula un program, îl desucrați la λs5 și apoi rulați acel program. Și cu această implementare, putem rula suita de testare a conformității JavaScript pentru a verifica dacă λs5 este corectă: de aceea se numește semantică testată. Și iată, λs5 trece porțiunea relevantă din suita de conformitate test262.

Problema

Totuși, fiecare postare de blog are nevoie de o problemă. Problema cu λs5 constă în deshidratare. Tocmai am afirmat că JavaScript este complicat, în timp ce limbajul de bază pentru λs5 este simplu. Acest lucru înseamnă că complicațiile JavaScript trebuie tratate nu în limbajul de bază, ci în deshidratare. Luați un exemplu ilustrativ. Iată câteva linii inocente de JavaScript:

Aceste linii de cuplu descarcă în următorul cod λs5:

Este cam mult. Este greu de citit și este greu de prelucrat de instrumente. Dar mai mult la obiect, λs5 este menit să fie folosit de cercetători, iar această umflare a codului a împiedicat cercetătorii care încearcă să o adopte. Vă puteți imagina că, dacă încercați să scrieți un instrument care funcționează peste cod λs5, și există o eroare în instrumentul dvs. și trebuie să-l depanați și trebuie să ștergeți atât de mult cod doar pentru cel mai simplu dintre exemple, este un pic de coșmar.

Soluția obișnuită

Deci, există prea mult cod. Din fericire există soluții bine cunoscute pentru această problemă. Am implementat o serie de tehnici standard de optimizare a compilatorului pentru a micșora codul λs5 generat, păstrând în același timp semantica acestuia. Iată o listă plictisitoare a optimizărilor de conservare a semanticii pe care le-am folosit:

  • Eliminarea codului mort
  • Pliere constantă
  • Propogare constantă
  • Propagarea aliasului
  • Conversia misiunii
  • Inlinierea funcției
  • Introduceți tipul și eliminați controalele statice
  • Curățați legăturile de mediu neutilizate

Cele mai multe dintre acestea sunt optimizări standard ale manualelor; deși ultimele două sunt specifice lui λs5. Oricum, am făcut toate acestea și am primit ... 5-10% contracție de cod.

Soluția extraordinară

Asta este: 5-10%.

Având în vedere amploarea problemei de umflare a codului, acest lucru nu este suficient de micșorat pentru a fi util. Deci, să facem un pas înapoi și să întrebăm de unde a venit toată această balonare. Am argumenta că umflarea codului poate fi împărțită în trei categorii:

  • Prevenirea umflării codului. O parte din acestea este intenționată. λs5 este un limbaj de bază mic și ar trebui să existe o oarecare extindere pe măsură ce îl traduceți.
  • Balonare de cod accidental. Funcția de desugaring de la JS la λs5 este o funcție simplă recursiv-descendentă. În mod intenționat, nu este inteligent și, ca urmare, generează uneori cod redundant. Și tocmai de asta scapă rescrierile care păstrează semantica de care tocmai am menționat.
  • Umflarea codului esențial. În cele din urmă, o anumită umflare a codului se datorează semanticii JS. JS este un limbaj complicat cu caracteristici complicate și se transformă în cod λs5 complicat.

Nu a fost mult de câștigat prin reducerea umflării intenționate sau accidentale a codului. Dar cum te descurci cu reducerea umflării codului Essential? Ei bine, umflarea esențială este umflarea codului care vine din complicațiile JS. Pentru a-l elimina, veți simplifica limba. Și am făcut exact asta! Am definit cinci transformări semantice:

  • (IR) Restaurarea identificatorului: pretindeți că JS are scop lexical
  • (FR) Restaurarea funcției: pretindeți că funcțiile JS sunt doar funcții și nu funcție-obiect-lucruri.
  • (FA) Aritate fixă: pretindeți că funcțiile JS iau întotdeauna atâtea argumente pe cât sunt declarate cu.
  • (EAU) Eliminarea afirmației: eliminați în siguranță unele verificări de rulare (codul dvs. este corect oricum, nu?)
  • (SAO) Simplificarea operatorilor aritmetici: eliminați comportamentul ciudat pentru operatorii de bază precum „+”.

Aceste transformări semantice care modifică blasfemează limbajul. De fapt, acest lucru este OK! Problema este că, dacă studiați JS sau faceți analize statice, probabil că nu vă ocupați deja de întreaga limbă. Este prea complicat, așa că, în schimb, vă ocupați de o limbă secundară. Și exact acest lucru surprinde aceste transformări care modifică semantica: sunt presupuneri simplificatoare despre limbajul JS.

Lecții despre JavaScript

Și putem afla despre JavaScript de la ei. Am implementat aceste transformări pentru λs5 și astfel am putea rula suita de teste cu transformările activate și să vedem câte teste s-au rupt. Acest lucru oferă o măsură brută de „corectitudine”: o transformare este 50% corectă dacă sparge jumătate din teste. Iată graficul:

Observați că transformările care modifică semantica reduc codul cu mai mult de 50%: acest lucru este mult mai bun decât 5-10% pe care le-au dat cele care păstrează semantica. Revenind la cele trei tipuri de umflare a codului, aceasta arată că majoritatea umflării codului în λs5 este esențială: provine din semantica complicată a JS și, dacă simplificați semantica, o puteți face să dispară.

În continuare, iată contracțiile fiecărei transformări care modifică semantica:

Deoarece aceste transformări care modifică semantica sunt simplificări ale semanticii JS, iar dimensiunea codului desugared este o măsură a complexității, puteți vedea acest grafic ca o măsură (brută!) A complexității caracteristicilor limbajului. În această lumină, observați IR (Restaurarea identificatorului): zdrobește celelalte transformări prin reducerea codului cu 30%. Aceasta arată că domeniul de aplicare al JavaScript-ului este complex: prin această valoare, 30% din complexitatea JavaScript-ului se datorează domeniului său de aplicare.

La pachet

Aceste transformări care modifică semantica dau restricții semantice asupra JS. Lucrarea noastră face aceste restricții precise. Și sunt exact genul de presupuneri simplificatoare pe care lucrările trebuie să le facă pentru a argumenta despre JS. Puteți chiar să descărcați λs5 din git și să implementați analiza dvs. peste λs5 cu un subset al acestor restricții activate și să îl testați. Deci, să lucrăm spre un viitor în care ziarele care vorbesc despre JS spun exact ce sub-limbă a JS înseamnă.

Hârtia

Acesta este doar un teaser: pentru a citi mai multe, consultați ziarul.