Jaké další jazyky EVM kromě Solidity stojí za pozornost?

Napsal: jtriley.ethjtriley.eth

Sestavil: 0x11, Foresight News

Ethereum Virtual Machine (EVM) je 256bitový, zásobníkový, globálně přístupný Turingův stroj. Vzhledem ke své výrazně odlišné architektuře od jiných virtuálních a fyzických strojů vyžaduje EVM doménově specifický jazyk DSL (poznámka: doménově specifický jazyk označuje počítačový jazyk, který se zaměřuje na určitou aplikační doménu).

V tomto článku se podíváme na současný stav v designu EVM DSL, který zahrnuje šest jazyků: Solidity, Vyper, Fe, Huff, Yul a ETK.

jazyková verze

  • Pevnost: 0,8,19

  • Vyper: 0.3.7

  • Fe: 0,21,0

  • Huff: 0,3,1

  • ETK: 0.2.1

  • Červenec: 0.8.19

Čtení tohoto článku vyžaduje, abyste měli základní znalosti o EVM, zásobníku a programování.

Přehled virtuálního stroje Ethereum

EVM je 256bitový Turingův stroj založený na zásobníku. Než se však ponoříme do jeho kompilátoru, měli bychom představit některé funkční vlastnosti.

Protože je EVM "Turing kompletní", bude trpět "problémem zastavení". Stručně řečeno, před spuštěním programu neexistuje způsob, jak určit, zda bude v budoucnu ukončen. Způsob, jakým EVM řeší tento problém, je měřit jednotky výpočtu pomocí „plynu“, který je obecně úměrný fyzickým zdrojům potřebným k provedení instrukcí. Množství Plynu na transakci je omezeno a iniciátor transakce musí zaplatit ETH úměrně k Plynu spotřebovanému transakcí. Jedním z dopadů této strategie je, že pokud existují dvě funkčně identické chytré smlouvy, bude více přijata ta, která spotřebuje méně plynu. Výsledkem jsou protokoly, které soutěží o extrémní účinnost plynu, přičemž inženýři se snaží minimalizovat spotřebu plynu pro konkrétní úkoly.

Navíc, když je volána smlouva, vytvoří se kontext provádění. V tomto kontextu má smlouva zásobník pro operace a zpracování, instanci lineární paměti pro čtení a zápis, místní trvalé úložiště pro čtení a zápis smlouvy a data připojená k volání „calldata“ lze číst, ale nelze je zaznamenávat. .

Důležitou poznámkou o paměti je, že i když neexistuje žádná definitivní „horní hranice“ její velikosti, je stále omezená. Cena plynu na rozšíření paměti je dynamická: jakmile je dosaženo prahové hodnoty, náklady na rozšíření paměti se kvadraticky zvyšují, což znamená, že cena plynu je úměrná druhé mocnině přidělení další paměti.

Smlouvy mohou také volat jiné smlouvy pomocí řady různých pokynů. Instrukce „call“ odešle data a volitelné ETH do cílové smlouvy, poté vytvoří svůj vlastní kontext provádění, dokud se plnění cílové smlouvy nezastaví. Direktiva "staticcall" je stejná jako "call", ale přidává kontrolu, která potvrzuje, že žádná část globálního stavu nebyla aktualizována před dokončením statického volání. A konečně, směrnice „delegatecall“ se chová jako „call“, s výjimkou toho, že zachovává některé informace o životním prostředí z předchozího kontextu. To se obvykle používá pro externí knihovny a smlouvy na zastoupení.

Proč na designu jazyka záleží

Jazyky specifické pro doménu (DSL) jsou nezbytné při interakci s atypickými architekturami. I když existují kompilátorové nástrojové řetězce, jako je LLVM, spoléhat se na ně při zvládání inteligentních kontraktů není ideální v situacích, kdy jsou kritické správnost programu a výpočetní efektivita.

Správnost programu je důležitá, protože smart kontrakty jsou ve výchozím nastavení neměnné a vzhledem k vlastnostem blockchainových virtuálních strojů (VM) jsou oblíbenou volbou pro finanční aplikace. I když existuje upgradovatelné řešení pro EVM, je to v nejlepším případě oprava a v nejhorším zranitelnost při spuštění libovolného kódu.

Výpočetní efektivita je také kritická, protože minimalizace výpočtů má ekonomické výhody, ale ne na úkor bezpečnosti.

Stručně řečeno, EVM DSL musí vyvážit správnost programu a účinnost plynu, dosáhnout jednoho nebo druhého provedením různých kompromisů bez obětování přílišné flexibility.

Přehled jazyků

Pro každý jazyk popíšeme jejich hlavní rysy a možnosti designu a zahrneme jednoduchou funkci počítání smart contract. Slovní popularita se určuje na základě údajů Total Value Locked (TVL) o Defi Llama.

Pevnost

Solidity je vysokoúrovňový jazyk, jehož syntaxe je podobná C, Javě a Javascriptu. Je to nejoblíbenější jazyk podle TVL, s TVL desetkrát vyšší než u dalšího nejoblíbenějšího jazyka. Pro opětovné použití kódu používá objektově orientovaný vzor, ​​kde se s inteligentními kontrakty zachází jako s objekty třídy a využívá vícenásobnou dědičnost. Kompilátor je napsán v C++ a v budoucnu se plánuje migrace na Rust.

Pole proměnných kontraktů jsou uložena v trvalém úložišti, pokud jejich hodnoty nejsou známy v době kompilace (konstanty) nebo v době nasazení (neměnné). Metody deklarované ve smlouvě mohou být standardně deklarovány jako čisté, prohlížené, splatné nebo neplatitelné, ale s modifikovatelným stavem. Čisté metody nečtou data z prováděcího prostředí a nemohou číst ani zapisovat do trvalého úložiště, to znamená, že při stejném vstupu budou čisté metody vždy vracet stejný výstup a nemají žádné vedlejší účinky. Metody zobrazení mohou číst data z trvalého úložiště nebo prostředí provádění, ale nemohou zapisovat do trvalého úložiště, ani nemohou vytvářet vedlejší efekty, jako je připojování protokolů transakcí. Placené metody mohou číst a zapisovat trvalé úložiště, číst data z prostředí provádění, vytvářet vedlejší efekty a mohou přijímat ETH připojené k volání. Neplatitelná metoda je stejná jako splatná metoda, ale má běhovou kontrolu, která potvrzuje, že v aktuálním kontextu provádění není připojeno žádné ETH.

Poznámka: Připojení ETH k transakci je oddělené od placení poplatků za plyn, připojené ETH je přijato smlouvou a můžete se rozhodnout jej přijmout nebo odmítnout obnovením kontextu.

Při deklaraci v rámci smlouvy mohou metody specifikovat čtyři modifikátory viditelnosti: private, internal, public nebo external. K soukromým metodám lze přistupovat interně pomocí instrukce „skok“ v rámci aktuální smlouvy. Žádná zděděná smlouva nemá přímý přístup k soukromým metodám. K interním metodám lze také přistupovat interně pomocí instrukce „skok“, ale zděděné smlouvy mohou používat interní metody přímo. K veřejným metodám lze přistupovat prostřednictvím externích smluv prostřednictvím instrukce „call“, která vytváří nový kontext provádění, a interně prostřednictvím skoků, když je metoda volána přímo. K veřejným metodám lze také přistupovat ze stejné smlouvy v novém kontextu provádění tak, že před volání metody přidáte „toto“. K externí metodě lze přistupovat pouze prostřednictvím instrukce „volání“, ať už je z jiné smlouvy nebo ze stejné smlouvy, před voláním metody je třeba přidat „toto“.

Poznámka: Instrukce "skok" ovládá čítač programu a instrukce "call" vytváří nový kontext provádění během provádění cílové smlouvy. Pokud je to možné, použijte „skok“ místo „volání“, abyste ušetřili benzín.

Solidity také poskytuje tři způsoby, jak definovat knihovny. První je externí knihovna, což je bezstavová smlouva, která je nasazena samostatně v řetězci, dynamicky propojená při volání smlouvy a přístupná prostřednictvím instrukce „delegatecall“. Toto je nejméně běžný přístup, protože podpora nástrojů pro externí knihovny je nedostatečná, "delegatecall" je drahý, musí načítat další kód z trvalého úložiště a vyžaduje více transakcí pro nasazení. Interní knihovny jsou definovány stejným způsobem jako externí knihovny s tím rozdílem, že každá metoda musí být definována jako interní metoda. V době kompilace je interní knihovna vložena do konečné smlouvy a během fáze analýzy mrtvého kódu jsou z knihovny odstraněny nepoužívané metody. Třetí způsob je podobný interní knihovně, ale namísto definování datových struktur a funkcí v rámci knihovny jsou definovány na úrovni souboru a lze je přímo importovat a použít ve finální smlouvě. Třetí přístup poskytuje lepší interaktivitu mezi člověkem a počítačem pomocí uživatelských datových struktur, aplikováním funkcí na globální rozsah a aplikací aliasových operátorů na určité funkce v omezené míře.

Kompilátor poskytuje dva optimalizační průchody. První je optimalizátor na úrovni instrukcí, který provádí optimalizační operace na finálním bajtkódu. Druhým je nedávné přidání použití jazyka Yul (více o tom později) jako přechodné reprezentace (IR) během procesu kompilace a následné provádění optimalizačních operací na generovaném kódu Yul.

Pro interakci s veřejnými a externími metodami ve smlouvě Solidity specifikuje standard Application Binary Interface (ABI) pro interakci s jejími smlouvami. V současné době je Solidity ABI považován za de facto standard pro EVM DSL. Ethereum ERC standardy specifikující externí rozhraní jsou implementovány v souladu se specifikací Solidity ABI a stylovým průvodcem. Ostatní jazyky se také řídí specifikací ABI Solidity s velmi malými odchylkami.

Solidity také poskytuje inline bloky Yul, což umožňuje nízkoúrovňový přístup k instrukční sadě EVM. Blok Yul obsahuje podmnožinu funkcí Yul, podrobnosti najdete v sekci Yul. To se obvykle používá pro optimalizaci plynu, využití funkcí, které nejsou podporovány syntaxí vyšší úrovně, a přizpůsobení úložiště, paměti a dat volání.

Vzhledem k popularitě Solidity jsou vývojářské nástroje velmi vyspělé a dobře navržené a Foundry v tomto ohledu vyniká.

Následuje jednoduchá smlouva napsaná v Solidity:

Vyper

Vyper je vysokoúrovňový jazyk se syntaxí podobnou Pythonu. Je to téměř podmnožina Pythonu s několika drobnými rozdíly. Je to druhý nejoblíbenější EVM DSL. Vyper je optimalizován pro zabezpečení, čitelnost, auditovatelnost a účinnost plynu. Nepoužívá objektově orientované vzory, vložené sestavení a nepodporuje opětovné použití kódu. Jeho kompilátor je napsán v Pythonu.

Proměnné uložené v trvalém úložišti jsou deklarovány na úrovni souboru. Pokud je jejich hodnota známa v době kompilace, mohou být deklarovány jako „konstantní“, pokud je jejich hodnota známa v době nasazení, mohou být deklarovány jako „neměnné“, pokud jsou označeny funkce pouze pro čtení pro proměnnou. K hodnotám konstant a invariantů se přistupuje interně prostřednictvím jejich názvů, ale k proměnným proměnným v trvalém úložišti lze přistupovat tak, že před název přidáte „self“. To je užitečné pro předcházení konfliktům jmenného prostoru mezi uloženými proměnnými, parametry funkcí a lokálními proměnnými.

Podobně jako Solidity i Vyper používá atributy funkcí k reprezentaci viditelnosti a variability funkcí. K funkcím označeným „@external“ lze přistupovat z externích smluv pomocí instrukce „call“. Funkce označené „@interní“ jsou přístupné pouze v rámci stejné smlouvy a musí mít předponu „self“. Funkce označené „@pure“ nemohou číst data z prostředí provádění nebo trvalého úložiště, zapisovat do trvalého úložiště ani vytvářet žádné vedlejší efekty. Funkce označené „@view“ mohou číst data z prostředí provádění nebo trvalého úložiště, ale nemohou zapisovat do trvalého úložiště ani vytvářet vedlejší efekty. Funkce označené „@payable“ mohou číst nebo zapisovat do trvalého úložiště, vytvářet vedlejší efekty a přijímat ETH. Funkce, které nedeklarují tento atribut proměnlivosti jako výchozí na non-payable, to znamená, že jsou stejné jako placené funkce, ale nemohou přijímat ETH.

Kompilátor Vyper se také rozhodne ukládat místní proměnné do paměti spíše než do zásobníku. Díky tomu jsou smlouvy jednodušší a efektivnější a řeší se problém „příliš hlubokého zásobníku“ běžný v jiných jazycích na vysoké úrovni. To však také přináší určité kompromisy.

Navíc, protože rozložení paměti musí být známé v době kompilace, musí být v době kompilace známa také maximální kapacita dynamických typů, což je omezení. Kromě toho může alokace velkého množství paměti vést k nelineární spotřebě plynu, jak je uvedeno v části Přehled EVM. V mnoha případech použití jsou však tyto náklady na plyn zanedbatelné.

Přestože Vyper nepodporuje inline sestavování, poskytuje více vestavěných funkcí, které zajišťují, že téměř každá funkce v Solidity a Yul je dostupná také ve Vyperu. K nízkoúrovňovým bitovým operacím, externím voláním a operacím proxy smlouvy lze přistupovat prostřednictvím vestavěných funkcí a lze implementovat vlastní rozvržení úložiště poskytnutím překryvných souborů v době kompilace.

Vyper nemá bohatou sadu vývojových nástrojů, ale má nástroje, které jsou těsněji integrované a lze je také zapojit do vývojových nástrojů Solidity. Mezi pozoruhodné nástroje Vyper patří interpret Titanaboa, který má mnoho vestavěných nástrojů souvisejících s EVM a Vyper pro experimentování a vývoj, a Dasy, Lisp založený na Vyperu s prováděním kódu v době kompilace.

Zde je jednoduchá smlouva napsaná ve Vyperu:

Fe

Fe je jazyk vysoké úrovně, jako je Rust, který je v současné době aktivně vyvíjen, přičemž většina funkcí ještě není k dispozici. Jeho kompilátor je primárně napsán v Rustu, ale jako jeho zprostředkující reprezentaci (IR) používá Yul a spoléhá se na optimalizátor Yul napsaný v C++. Očekává se, že se to změní přidáním Sonatiny, nativního backendu Rust. Fe používá moduly pro sdílení kódu, takže nepoužívá objektově orientovaný vzor, ​​ale znovu používá kód prostřednictvím systému založeného na modulech, kde jsou proměnné, typy a funkce deklarovány v modulech a lze je importovat podobným způsobem jako Rust.

Proměnné trvalého úložiště jsou deklarovány na úrovni smlouvy a nejsou veřejně přístupné bez manuálně definovaných funkcí získávání. Konstanty mohou být deklarovány na úrovni souboru nebo modulu a přístupné uvnitř smlouvy. Neměnné proměnné doby nasazení nejsou aktuálně podporovány.

Metody lze deklarovat na úrovni modulu nebo v rámci smlouvy a ve výchozím nastavení jsou čisté a soukromé. Aby byla metoda kontraktu veřejná, musí být před definici přidáno klíčové slovo „pub“, což ji zpřístupní externě. Chcete-li číst z proměnné trvalého úložiště, musí být první parametr metody "self". Chcete-li číst a zapisovat do trvalého úložiště, musí být prvním parametrem "mut self". Klíčové slovo "mut" označuje, že úložiště kontraktu je proměnlivé během provádění metody. Přístup k proměnným prostředí se provádí předáním parametru "Context" metodě, obvykle pojmenované "ctx".

Funkce a vlastní typy lze deklarovat na úrovni modulu. Ve výchozím nastavení jsou položky modulu soukromé a nelze k nim přistupovat, pokud není použito klíčové slovo „pub“. Nezaměňujte však s klíčovým slovem „hospoda“ na úrovni smlouvy. Veřejní členové modulu jsou přístupní pouze v rámci konečné smlouvy nebo jiných modulů.

Fe v současné době nepodporuje inline sestavení, místo toho jsou instrukce zabaleny vnitřními prvky kompilátoru nebo speciálními funkcemi, které se převádějí na instrukce v době kompilace.

Fe následuje Rustův syntax a typový systém, podporuje typové aliasy, výčty s podtypy, rysy a generiky. Podpora je v současné době omezená, ale pracuje se na ní. Vlastnosti lze definovat a implementovat pro různé typy, ale generika a omezení vlastností nejsou podporována. Výčty podporují podtypy a metody na nich lze implementovat, ale nelze je zakódovat do externích funkcí. Ačkoli je systém typu Fe stále ve vývoji, ukazuje velký potenciál pro psaní bezpečnějšího kódu kontrolovaného během kompilace pro vývojáře.

Zde je jednoduchá smlouva napsaná ve Fe:

Zlost

Huff je jazyk symbolických instrukcí s ručním řízením zásobníku a minimální abstrakcí instrukční sady EVM. Prostřednictvím direktivy "#include" lze během kompilace analyzovat všechny zahrnuté soubory Huff, aby se dosáhlo opětovného použití kódu. Původně napsaný aztéckým týmem pro extrémně optimalizované algoritmy eliptických křivek, kompilátor byl později přepsán v TypeScriptu a poté v Rustu.

Konstanty musí být definovány v době kompilace, neměnné nejsou aktuálně podporovány a trvalé proměnné úložiště nejsou v jazyce explicitně definovány. Protože pojmenované proměnné úložiště jsou abstrakcí na vysoké úrovni, zápis do trvalého úložiště v Huff se provádí pomocí operačních kódů "sstore" pro zápis a "sload" pro čtení. Vlastní rozložení úložiště může definovat uživatel, nebo se můžete řídit konvencí začít od nuly a zvyšovat každou proměnnou pomocí vestavěného „FREE_STORAGE_POINTER“ kompilátoru. Zpřístupnění uložené proměnné externě vyžaduje ruční definování cesty kódu, která dokáže číst a vrátit proměnnou volajícímu.

Externí funkce jsou také abstrakce zavedené jazyky na vysoké úrovni, takže v Huffu neexistuje žádný koncept externích funkcí. Většina projektů se však v různé míře řídí specifikacemi ABI jiných jazyků na vysoké úrovni, nejčastěji Solidity. Běžným vzorem je definování "dispečera", který načte nezpracovaná data volání a použije je ke kontrole odpovídajících selektorů funkcí. Pokud se shoduje, provede se jeho následný kód. Protože jsou plánovače definovány uživatelem, mohou se řídit různými vzory plánování. Solidity řadí selektory ve svých plánovačích abecedně podle názvu, Vyper je řadí numericky a provádí binární vyhledávání za běhu a většina plánovačů Huff třídí podle očekávané frekvence využití funkcí, zřídkakdy používá tabulky skoků. V současné době nejsou skokové tabulky nativně podporovány v EVM, takže k jejich implementaci je třeba použít introspekci, jako je „kopírování kódu“.

Interní funkce jsou definovány pomocí direktivy #definefn", která může přijímat parametry šablony pro zvýšení flexibility a specifikovat očekávanou hloubku zásobníku na začátku a na konci funkce. Protože jsou tyto funkce interní, nelze k nim přistupovat zvenčí. Vnitřní přístup vyžaduje použití instrukce „skok“.

Jiné řídicí toky, jako jsou podmíněné příkazy a příkazy smyčky, mohou používat definice cíle skoku. Cíl skoku je definován identifikátorem následovaným dvojtečkou. Skoky na tyto cíle lze provést vložením identifikátoru do zásobníku a provedením instrukce skoku. To je vyřešeno posunem bajtového kódu v době kompilace.

Makra jsou definována pomocí #definemacro" a jsou jinak stejná jako interní funkce. Klíčový rozdíl je v tom, že makro negeneruje instrukci „skoku“ v době kompilace, ale místo toho zkopíruje tělo makra přímo do každého volání v souboru.

Tento návrh vyrovnává snížení libovolných skoků oproti nákladům na běhový plyn na úkor větší velikosti kódu při vícenásobném volání. Makro "MAIN" je považováno za vstupní bod smlouvy a první instrukce v jeho těle se stane první instrukcí v bajtovém kódu za běhu.

Mezi další funkce zabudované do kompilátoru patří generování hash událostí pro protokolování, selektory funkcí pro plánování, selektory chyb pro zpracování chyb a kontrola velikosti kódu pro interní funkce a makra.

Poznámka: Komentáře k zásobníku jako "//[počet]" nejsou povinné, používají se pouze k označení stavu zásobníku na konci provádění řádku.

Zde je jednoduchá smlouva napsaná v Huff:

ETK

EVM Toolkit (ETK) je jazyk symbolických instrukcí s manuální správou zásobníku a minimálními abstrakcemi. Kód lze znovu použít prostřednictvím direktiv "%include" a "%import" a kompilátor je napsán v Rustu.

Jeden významný rozdíl mezi Huffem a ETK je ten, že Huff přidává do initkódu mírnou abstrakci, známou také jako kód konstruktoru, kterou lze přepsat definicí speciálního makra „CONSTRUCTOR“. V ETK se tyto neabstrahují, initcode a runtime kód musí být definovány společně.

Podobně jako Huff, ETK čte a zapisuje trvalé úložiště pomocí instrukcí „sload“ a „sstore“. Neexistuje však žádné konstantní nebo neměnné klíčové slovo, ale konstanty lze simulovat pomocí jednoho ze dvou maker v ETK, výraz makro. Makra výrazů se nerozkládají na instrukce, ale místo toho generují číselné hodnoty, které lze použít v jiných instrukcích. Například nemusí vygenerovat celý příkaz "push", ale může vygenerovat číslo, které se má zahrnout do příkazu "push".

Jak již bylo zmíněno dříve, externí funkce jsou konceptem jazyka na vysoké úrovni, takže odhalení cesty kódu externě vyžaduje vytvoření plánovače selektoru funkcí.

Interní funkce nejsou explicitně definovány jako v jiných jazycích. To také umožňuje další řídicí toky, jako jsou smyčky a podmíněné příkazy.

ETK podporuje dva typy maker. První je výrazové makro, které přijímá libovolný počet argumentů a vrací číselnou hodnotu, kterou lze použít v jiných instrukcích. Výrazová makra negenerují instrukce, ale místo toho generují okamžité hodnoty nebo konstanty. Makra direktiv však přijímají libovolný počet argumentů a generují libovolný počet direktiv v době kompilace. Instrukční makra v ETK jsou podobná makrům Huff.

Následuje jednoduchá smlouva napsaná v ETK:

Yul

Yul je jazyk symbolických instrukcí s tokem řízení na vysoké úrovni a velkým počtem abstrakcí. Je součástí Solidity toolchain a může být volitelně použit v Solidity build passech. Yul nepodporuje opětovné použití kódu, protože má být spíše cílem kompilace než samostatným jazykem. Jeho kompilátor je napsán v C++ a plánuje se jeho migrace na Rust spolu se zbytkem kanálu Solidity.

V Yulu je kód rozdělen na objekty, které mohou obsahovat kód, data a vnořené objekty. Proto v Yulu nejsou žádné konstanty ani externí funkce. Je třeba definovat dispečery selektoru funkcí, aby byly cesty kódu vystaveny vnějšímu světu.

S výjimkou instrukcí zásobníku a řídicího toku je většina instrukcí v Yul vystavena jako funkce. Instrukce mohou být vnořené, aby se zkrátila délka kódu, nebo přiřazeny dočasným proměnným a poté předány dalším instrukcím k použití. Podmíněné větve mohou používat blok „if“, který se provede, pokud je hodnota nenulová, ale neexistuje žádný blok „jinak“, takže zpracování více kódových cest vyžaduje použití „přepínače“ pro zpracování libovolného počtu případů a "výchozí" záložní možnost. Smyčky lze provádět pomocí smyčky „for“, přestože se její syntaxe liší od jiných vysokoúrovňových jazyků, poskytuje stejné základní funkce. Interní funkce lze definovat pomocí klíčového slova "function" a jsou podobné definicím funkcí ve vyšších jazycích.

Většina funkcí v Yul je vystavena v Solidity pomocí inline montážních bloků. To umožňuje vývojářům rozbít abstrakce a psát vlastní funkce nebo používat funkce Yul, které nejsou dostupné v syntaxi na vysoké úrovni. Použití této funkce však vyžaduje hluboké pochopení chování Solidity s ohledem na data volání, paměť a úložiště.

Existují také některé jedinečné funkce. Funkce "datasize", "dataoffset" a "datacopy" fungují na objektech Yul prostřednictvím jejich aliasů řetězců. Funkce "setimmutable" a "loadimmutable" umožňují nastavení a načtení neměnných parametrů v konstruktoru, i když jejich použití je omezené. Funkce "memoryguard" označuje, že je přidělen pouze daný rozsah paměti, což umožňuje kompilátoru používat paměť mimo chráněný rozsah pro další optimalizace. Konečně „doslovně“ umožňuje použití instrukcí, které kompilátor Yul nezná.

Zde je jednoduchá smlouva napsaná v Yulu:

Vlastnosti dobrého EVM DSL

Dobrý EVM DSL by se měl poučit z výhod a nevýhod každého zde uvedeného jazyka a měl by také zahrnovat základy, které se nacházejí téměř ve všech moderních jazycích, jako jsou podmíněné podmínky, porovnávání vzorů, smyčky, funkce a další. Kód by měl být explicitní, s minimálními implicitními abstrakcemi přidanými kvůli kráse kódu nebo čitelnosti. Ve vysokých sázkách, v prostředích kritických pro správnost by měl být každý řádek kódu jednoznačně interpretovatelný. Kromě toho by měl být jádrem každého skvělého jazyka dobře definovaný modulový systém. Mělo by jasně uvádět, které položky jsou definovány v jakém rozsahu a které jsou přístupné. Každá položka v modulu by měla být ve výchozím nastavení soukromá a pouze explicitně veřejné položky by měly být veřejně přístupné externě.

V prostředí s omezenými zdroji, jako je EVM, je efektivita důležitá. Efektivity je často dosaženo poskytováním levných abstrakcí, jako je provádění kódu v době kompilace prostřednictvím maker, bohatý typový systém pro vytváření dobře navržených opakovaně použitelných knihoven a obaly pro běžné interakce v řetězci. Makra generují kód v době kompilace, což je užitečné pro snížení standardního kódu pro běžné operace a v případech, jako je Huff's, kde je lze použít ke kompromisu mezi velikostí kódu a efektivitou za běhu. Bohatý typový systém umožňuje expresívnější kód, více kontrol v době kompilace pro zachycení chyb před spuštěním a v kombinaci s vnitřními prvky kompilátoru s typovou kontrolou může eliminovat potřebu velké části inline sestavování. Generika také umožňují zabalit hodnoty s nulovou hodnotou (jako je externí kód) do typů „možností“ nebo operace náchylné k chybám (jako jsou externí volání) zabalit do typů „výsledků“. Tyto dva typy jsou příklady toho, jak autoři knihoven nutí vývojáře zpracovávat každý výsledek definováním cest kódu nebo transakcí, které obnovují neúspěšné výsledky. Mějte však na paměti, že se jedná o abstrakce v době kompilace, které se za běhu vyřeší na jednoduché podmíněné skoky. Nucení vývojářů zpracovávat každý výsledek v době kompilace prodlužuje počáteční dobu vývoje, ale výhodou je, že za běhu dochází k mnohem méně překvapením.

Flexibilita je také důležitá pro vývojáře, takže zatímco výchozí pro složité operace by měla být bezpečná a potenciálně méně efektivní cesta, někdy je nutné použít efektivnější cesty kódu nebo nepodporované funkce. Za tímto účelem by inline montáž měla být otevřená vývojářům bez zábradlí. Inline sestava Solidity nastavuje určité mantinely pro jednoduchost a lepší průchody optimalizátorem, ale když vývojáři potřebují plnou kontrolu nad prostředím provádění, měli by jim být tato práva udělena.

Některé potenciálně užitečné funkce zahrnují schopnost manipulovat s vlastnostmi funkcí a dalších položek v době kompilace. Například atribut „inline“ může zkopírovat tělo jednoduché funkce do každého volání, místo aby vytvářel více skoků pro efektivitu. Atribut „abi“ vám umožňuje ručně přepsat ABI generované danou externí funkcí a přizpůsobit se jazykům s různými styly kódování. Kromě toho lze definovat volitelný plánovač funkcí, který umožňuje přizpůsobení v rámci jazyka na vysoké úrovni pro další optimalizace cest kódu, u kterých se očekává, že budou častěji používány. Před provedením příkazu "name" zkontrolujte například, zda je selektor "přenos" nebo "přenosZ".

na závěr

Návrh EVM DSL má před sebou dlouhou cestu. Každý jazyk má svá vlastní jedinečná designová rozhodnutí a těším se, jak se budou v budoucnu vyvíjet. Jako vývojáři je v našem nejlepším zájmu naučit se co nejvíce jazyků. Za prvé, naučení se více jazyků a pochopení jejich rozdílů a podobností prohloubí naše chápání programování a základní architektury stroje. Za druhé, jazyk má hluboké síťové efekty a silné retenční vlastnosti. Není pochyb o tom, že velcí hráči budují své vlastní programovací jazyky, od C#, Swift a Kotlin po Solidity, Sway a Cairo. Naučit se plynule přepínat mezi těmito jazyky poskytuje bezkonkurenční flexibilitu pro kariéru softwarového inženýrství. Nakonec je důležité pochopit, že za každým jazykem je spousta práce. Nikdo není dokonalý, ale bezpočet talentovaných lidí vynakládá velké úsilí na vytvoření bezpečných a příjemných zážitků pro vývojáře, jako jsme my.