Uploaded avatar of iHiD

Itt a Mechanikus március!

@iHiD
Több, mint 3 éve ezelőtt
Videó

Üdv a második tematikus hónapunkban, a Mechanical Marchben. Ebben a hónapban a rendszernyelvekre összpontosítunk, azokra, amelyek gépi kódra fordulnak le.

Ez részben bejegyzés, részben a Mechanical March videó átirata. Adok egy gyors bevezetőt a hónaphoz, aztán egy kicsit megnézzük a rendszernyelveket, a történelmi fejlődésüket, a gépi kódra fordítás előnyeit és hátrányait, valamint röviden mindegyik kiemelt nyelvet. Ismét velem van Erik, aki a második felében fog a legtöbbet beszélni. De először én kezdem a gyakorlati információkkal.

Tehát először is a hónap kiemelt nyelvei. Ezek a C, C++, D, Go, Nim, Rust, V és Zig. Ahhoz, hogy megkapd a Mechanical March jelvényt, öt feladatot kell teljesítened valamelyik ilyen nyelvben. A Go-kurzusunk tanterve az egyik legjobb az Exercism-ön, szóval igazán ajánlom, hogy próbáld ki. Az Exercism-nél mi is nagy rajongói vagyunk a Nimnek, mivel viszonylag egyszerű nyelv a kezdéshez és nagyon könnyű benne írni, szóval mindenképp ajánlom, hogy azt is próbáld ki.

Öt kiemelt feladatot is kipróbálhatsz:

  • linked-list vagy simple-linked-list (nyelvtől függően): memóriafoglalás/felszabadítás, mutatók
  • secret-handshake: bitműveletek
  • pangram: for ciklusok, stringek és karakterek
  • sieve: tömbök, for ciklusok
  • binary-search: tömbök, ciklusok

Van egy új jelvény is, amit a néhány nappal ezelőtti frissítési videóban jelentettem be, és az öt kiemelt feladat teljesítéséért jár a tematikus nyelvekben. Tehát hogy megkapd azt a jelvényt, az év során valamikor mindegyik ilyen nyelvben teljesítened kell a feladatokat.

Sok izgalmas dolog is készülődik: interjúk a Go Core csapat néhány tagjával, és remélhetőleg a Rustból és néhány más nyelvből is. A hónap során rengeteg élő közvetítés is lesz. És hamarosan érkezik némi Mechanical March ajándéktárgy is!

Szóval ássunk bele egy kicsit jobban a technikai oldalba.

Mire használják ezeket a nyelveket?

Nos, az Exercism-ön mindenhol használjuk őket. A CLI-nk Go-ban íródott, a configlet nevű belső kurzuskezelő eszközünk Nimben, és a kulcsfontosságú könyvtár, ami megszámolja a megoldásaidban a kódsorokat, Rustban íródott. Erik, miért választottuk ezeket a nyelveket ezekhez az eszközökhöz?

CLI:

  • Szerintem azért használtuk a Go-t, mert Katrina azzal volt a legjobban tisztában.
  • A Go remek az ilyen kisebb parancssori eszközökhöz.
  • A Go-kód viszonylag egyszerű, ami megkönnyíti a közreműködést
  • A Go binárisok könnyen telepíthetők, mivel nem igényelnek futtatókörnyezetet
  • A Go jól kezeli a keresztfordítást

Nim

  • A Nimnek nagyjából ugyanazok az előnyei, mint a Go-nak
  • Nem volt sok olyan ember, aki ismerte volna a Go-t és karban tudta volna tartani a CLI-t, ezért váltottunk a Nimre

Milyen más helyeken számítanál arra, hogy ezeket a nyelveket használják?

  • Bárhol, ahol a teljesítmény fontos (illesztőprogramok, játékok, operációs rendszerek, buildrendszerek/fordítók)
  • Bárhol, ahol korlátozottak az erőforrások (pl. beágyazott szoftverek)
  • Bármi, aminek nagyon hordozhatónak kell lennie, azaz: sok különböző platformon fusson

Mi a gépi kód?

Ahogy korábban mondtam, a Mechanical March nyelveit az különbözteti meg, hogy gépi kódra fordulnak. El tudnád magyarázni egy kicsit, mi a gépi kód, és ezzel szemben mi a bájtkód?

  • A gépi kód olyan kód, ami közvetlenül futni tud a gépen.
  • A bájtkód ezzel szemben más kódot igényel ahhoz, hogy a bájtkódot gépi kódra értelmezze/fordítsa. Ezért a bájtkód végrehajtás előtt egy közbenső lépést igényel.

Mik a gépi kód előnyei és hátrányai a bájtkóddal szemben?

Előnyök:

  • Gyorsabb indulás (nincs JIT-fordítási lépés)
  • Kisebb memóriaigény (nem töltődik be futtatókörnyezet, nincs bájtkód a memóriában, remek beágyazott rendszerekhez)
  • A célgépnek nem kell futtatókörnyezetet telepítenie (fontos a docker konténerek kicsiben tartásához)

Hátrányok:

  • Nem hordozható. A bájtkód hordozható, de a lefordított gépi kód platformspecifikus
  • Nem lehet olyan fejlett optimalizálásokat végezni, mint a profilvezérelt optimalizálás (annak meghatározása, hogyan érdemes a legjobban lefordítani a (bájt)kódot, miután egy ideig futott)

Megjegyzés: hibrid megközelítések is lehetségesek, ahol a nyelv bájtkódra fordul, majd egy másik eszközzel fordítja le azt a bájtkódot gépi kódra.

A rendszerprogramozás fejlődése

Oké. Nézzük meg egy kicsit a hónap néhány nyelvének fejlődését. Először is kezdjük a legelején, a C-vel, és azzal, hogyan fejlődött ki belőle a C++. Mesélj egy kicsit ezekről a nyelvekről

A C egy nagyon alacsony szintű nyelv. Úgy érzed, mintha csak kicsit lennél a gépi kód felett. Ez nagyon erőssé és jól optimalizálhatóvá teszi, de kissé hajlamossá is a bugokra (pl. null pointer kivételek és puffertúlcsordulások). A memóriakezelés teljesen manuális, így a programozó felelőssége, ami potenciálisan bugokhoz és/vagy memóriaszivárgásokhoz vezethet. A C++ olyan, mint a C, de támogatja az objektumorientált programozást. Még mindig meglehetősen alacsony szintű, és manuális memóriakezelést igényel. A C és a C++ is lehetővé teszi, hogy inline assemblyt (ASM) írj!

És mi a helyzet az újabb rendszernyelvekkel? Hogyan fejlődtek?

Minden modern rendszerprogramozási nyelv támogatja az automatikus memóriakezelést, akár referenciaszámlálással, szemétgyűjtővel vagy valamilyen más mechanizmussal.

A korai rendszerprogramozási nyelvek mind támogatják a null pointereket (amit Tony Hoare az „egymilliárd dolláros tévedésének” nevezett). Ezek hírhedtek arról, hogy futásidejű hibákhoz és sebezhetőségekhez vezetnek. Sok modern nyelv megszabadul a null-tól, vagy legalábbis erőfeszítést igényel a használata.

Egy másik változás, hogy a módosítható értékek alapértelmezettségétől elmozdultak az alapértelmezés szerint megváltoztathatatlan értékek felé. Pl. a Rustban és a VLangban is alapértelmezés szerint megváltoztathatatlanok az értékek, és külön kell kérni a módosíthatóságot.

Minden újabb nyelv támogatja a C-vel (vagy C++-szal) való együttműködést, mivel rengeteg kód íródott ezeken a nyelveken.

Egy másik érdekesség, hogy néhány újabb nyelv nem közvetlenül fordít gépi kódra, hanem más eszközöket használ erre. Pl. a Rust és a Zig az LLVM-et használja, míg a Nim sokféle fordító használatát teszi lehetővé. Ezt nevezik transzpilálásnak.

És mi a helyzet az olyan dolgokkal, mint a makrók és a metaprogramozás?

Érdekes megosztottság van a makrók/metaprogramozás terén. A C/C++ makrói erősek, de némileg kétes hírnevük van, mert nehéz velük dolgozni. A Rust, a Nim és a D mind erős metaprogramozást kínál, de sokkal szebb módon. Ezzel szemben a VLang és a Zig kifejezetten a nyelv jellemzőjeként említi, hogy nincsenek makrók, a Go pedig más megközelítést alkalmaz a go generate-nel.

A rendszernyelvekről az a hír járja, hogy meglehetősen alacsony szintűek. Ez még mindig igaz?

Az újabb nyelvek mind magasabb absztrakciós szinten működnek, mint a C/C++. Pl. a Rust, a D és a Nim lehetővé tesz egy nagyon funkcionális kódírási módot is. A Nimben és a D-ben még a „tiszta” függvények fogalma is megvan.

Bevezetés a hónap nyelveibe

Jó lenne egyenként megnézni a nyelveket. Mindegyik nyelv hasonlít: mind erősen és statikusan típusos. De nézzük meg, miben térnek el. Kezdjük a C-vel?

C

  • Dennis Ritchie fejlesztette
  • Az egyik legrégebbi, és valószínűleg a világ legtöbbet használt nyelve
  • Rengeteg szoftver íródott C-ben, például a Unix és a Linux
  • Nagyon nagy hatású (gondolj csak arra, hogy léteznek C-szerű nyelvek)
  • Manuális memóriakezelés
  • Nagyon jó teljesítményű (közel a hardverhez)
  • Bárhol fut
  • Ideális beágyazott rendszerekhez
  • Meglehetősen kicsi nyelv

C++

  • Bjarne Stroustrup fejlesztette
  • A C utódja, de hozzáadott objektumorientáltsággal (C osztályokkal)
  • Segített népszerűsíteni az objektumorientált programozást
  • Több magas szintű nyelvi funkció, mint a C-ben
  • Támogatja a generikus programozást sablonokon keresztül
  • Modultámogatás névtereken keresztül
  • Sok játék(motor) íródott C++-ban, és a Windows nagy része is
  • Manuális memóriakezelés
  • Még mindig fejlődik, rendszeresen kerülnek bele új funkciók (nagy specifikáció)

D

  • Walter Bright fejlesztette, később Andrei Alexandrescu is csatlakozott
  • Eredetileg újramérnökölt C++-nak szánták (tanulva annak „hibáiból”), de sok más nyelvből is merít inspirációt
  • Több paradigmát támogat: imperatív/objektumorientált/funkcionális programozást
  • Könnyű együttműködés a C/C++-szal
  • Egységes függvényhívási szintaxis
  • Fordítási idejű függvénykiértékelés (pl. reguláris kifejezés állapotgépének generálása fordítási időben)
  • Támogatja a funkcionális programozást és a „tiszta” függvényeket
  • Sok biztonsági funkció
  • Memóriabiztonság @safe segítségével
  • Szerződések (elő-/utófeltételek, invariánsok)
  • Tiszta függvények
  • Fókusz az egységteszteken, a tesztek a tesztelt forráskód mellett vannak (ez kivételt igényelt az Exercism weboldalán :))
  • Unicode

Rust

  • A Mozilla Research alkalmazottja, Graydon Hoare fejlesztette, majd a Mozilla hivatalosan is felkarolta, és ma a Rust Foundation része
  • Több paradigmát támogat: OO (de véleményvezérelt módon, pl. nincs öröklődés), imperatív és funkcionális programozást (Option/Result típusok, mintaillesztés)
  • Sok új eszköz íródott Rustban (pl. SWC, de a Gleam is, és a Linux kernel második támogatott nyelve; mi is rá támaszkodunk a kódszámlálóhoz)
  • Fókusz a megbízhatóságon és a teljesítményen
  • Az LLVM-re épül
  • A StackOverflow felmérésén az elmúlt 7 évben a legkedveltebb nyelv
  • Gyors, részben a minimális mag és standard könyvtár miatt
  • Biztonságos, mind memóriabiztonság, mind szálbiztonság terén, tulajdonlás és élettartamok révén, alapértelmezés szerint megváltoztathatatlan
  • Erős típusrendszer, ami sok bugot elkap fordítási időben (különösen a memóriával kapcsolatosakat). A fordító igazán hasznos hibaüzeneteket ad
  • Minden benne van: fordító, build eszköz, formázó, csomagkezelő, IDE-integrációk
  • Remek dokumentáció (még egy hatalmas dokumentum is van arról, hogyan működik a fordító)
  • Hordozható: egyetlen statikus binárisra fordul, és nem igényel telepített futtatókörnyezetet
  • Könnyű együttműködés a C kóddal
  • Nulla költségű absztrakciók
  • Félelem nélküli párhuzamosság
  • Makrók

Nim

  • Andreas Rumpf fejlesztette (eredetileg Nimrod volt a neve)
  • A szintaxisa a Pythonból merít ihletet
  • Több paradigmát támogat
  • Az Exercism-ön a configletben használjuk
  • Kiváló teljesítmény: nulla többletköltségű iterátorok, az értékalapú típusok veremallokációjának előnyben részesítése
  • Modern, kifejező típusrendszer: típuskövetkeztetés, tuple-ök, generikusok, összegtípusok, async/await
  • Szemétgyűjtés, de determinisztikus memóriakezelés támogatásával (több memóriakezelési lehetőség)
  • Fordítási idejű kódfuttatás
  • Egységes hívási szintaxis
  • Makrók: könnyű bővíteni a nyelvet
  • Hatásrendszer: mellékhatások kódolása a típusrendszerben

Go

  • Robert Griesemer, Rob Pike és Ken Thompson fejlesztette a Google-nél
  • Többnyire imperatív/procedurális, objektumorientált jellegű megközelítést támogat (de öröklődés nélkül)
  • Nagy projektekben használják, mint a Docker vagy a Kubernetes. Remek backendekhez és CLI-khez is (pl. esbuild)
  • Célja, hogy elég egyszerű legyen ahhoz, hogy fejben tartsd (kevés szintaxis)
  • Memóriabiztonság szemétgyűjtővel
  • Gyors: gyors fordítás, gyors tesztelés és gyors futásidő. Beépített támogatás benchmarkok írásához!
  • Véleményvezérelt: sok erőfeszítés irányul a Go-kód stílusának befolyásolására: kevés szintaxis, a kód formázása a go fmt-tal, eszközök a kód idiomatikus használatának ellenőrzésére, hibát jelez olyan dolgokra, amik más nyelvekben csak figyelmeztetések (pl. nem használt változók), dokumentáció, amely felsorolja a követendő Go-idómákat
  • Könnyű típusrendszer, ami nagyon rugalmassá teszi a Go-t (támogatja a típuskövetkeztetést)
  • Hordozható: egyetlen statikus binárisra fordul, és nem igényel telepített futtatókörnyezetet. Könnyű keresztfordítás. Párhuzamosság goroutine-okkal (könnyűsúlyúak) és kommunikáció csatornákon keresztül
  • Strukturális típusozás interfészeken keresztül (hasonló a duck typinghoz, de statikusan ellenőrzött)
  • Hibakezelés: a nyelv arra ösztönöz, hogy ellenőrizd és kezeld a hibákat

VLang

  • Alexander Medvednikov és Delyan Angelov fejlesztette
  • A Goból merít ihletet:
  • Ugyanaz a „csak egyféleképpen csináljuk a dolgokat” stratégia
  • Ugyanaz a „minimális szintaxis” stratégia
  • Korutinok
  • Eltér a Go-tól:
  • Nincs nil/null, hanem result típust használ
  • Alapértelmezés szerint megváltoztathatatlan
  • Összegtípusok (funkcionális)
  • String-interpoláció
  • Kisebb futásidejű környezet/binárisok
  • Mintaillesztés
  • Azon dolgoznak, hogy a GC opcionális legyen (autofree)
  • Nulla költségű C-együttműködés
  • Dokumentáció generálása a kódból
  • Gyors fordító, ami kevés memóriát használ

Zig

  • Andrew Kelley fejlesztette
  • Meglehetősen kevés szintaxis (500 soros PEG nyelvtani fájl)
  • Fordítási idejű kódfuttatás és reflexió
  • Célja, hogy „nyilvánvaló” legyen: nincs rejtett vezérlési folyam, allokáció vagy makrók/metaprogramozás
  • Manuális memóriaallokáció
  • Különböző allokátorokat támogat
  • A standard könyvtár azon függvényei, amelyek allokálnak, allokátor paramétert kapnak
  • A tesztkeretrendszer képes felismerni a memóriaszivárgásokat
  • Biztonság:
  • A hibák értékek, és kezelni kell őket
  • Nincs null, opcionális típust használ
  • A tesztek ugyanabba a fájlba írhatók, mint a forráskód (mint a D-ben)
  • Az LLVM-et használja backendként
  • Képes C/C++ kódot fordítani
  • Könnyű C-együttműködés
  • Könnyű keresztfordítás

Összegzés

Itt most abbahagyjuk, mert gondolom, mindenkinek eléggé tele van már a feje ezen a ponton.

Remélhetőleg ez egy hasznos és szórakoztató bevezető volt a hónap kiemelt nyelveihez. Remélem, rengeteg örömöd telik majd ezeknek a nyelveknek a felfedezésében. Eriket és engem is nagyon érdekelne, melyikeket választod, és hogy tetszenek, szóval mindenképp írj a hozzászólásokban vagy a fórumon!

Köszönjük a figyelmet!

1. Mar 2023 · Hasznosnak találtad?