diff --git a/BASED.md b/BASED.md index f545bad..3b4ebf5 100644 --- a/BASED.md +++ b/BASED.md @@ -2,6 +2,10 @@ Ha megcsinálom a "strukturált formában tárolt programszöveges" ötletem, akkor egy saját nyelvet is implementálnák vele, ami "nem memory safe, hanem nehezebbé teszi a memória hibákat" - tehát nem lenne bonyolult borrow checker hozzá. +Megj.: Május elsején kéne az első igazi változatát megcsinálni? Ugyanis a BASIC akkor futtatott először kódot: + + https://prog.hu/hirek/6677/basic-60-eves-programozasi-nyelv-szuletesnap-evfordulo-darthmouth-kemeny + # Elnevezések - *.basd @@ -10,12 +14,45 @@ Ha megcsinálom a "strukturált formában tárolt programszöveges" ötletem, ak - Based Advanced Simple Effective Devlang - - - - - +Based - szleng: olyan önálló vélemény vagy tett, ami ki mer állni magáért és nem feltétlenül a trendeket követi, és ezt nem fél kinyilatkozni. Azaz van egy alapja/bázisa (base) annak amit vallasz, ettől leszel bázisolt (based). Nem feltétlenül elfogult vélemény, de mivel a tömeggel nem mindig ért egyet, ezért azt sokan vitathatják. + +Magyarul: "Megalapozott" vagy esetleg "Alapos" / "Alapozott" - Magyar szlengben (másra) még használt: "Alap" (alap, hogy ismerem!) + +Based - Slang: true to one’s self or secure in one’s beliefs regardless of what others think, especially when those beliefs fall outside of the mainstream. + +For ex.: + + Her presentation was so based—did you see the teacher's expression? Priceless! + # Operátor precedencia https://www.youtube.com/watch?v=fIPO4G42wYE ^^Ez baromi hasznos és szerintem nem csak rekurzív leszállásnál lehet szerintem ez! https://www.youtube.com/watch?v=AikEFhCaZwo +1. Zárójel, adattag-hozzáférés, függvényhívás, konstruktor, alaptípus-konverzió, postfix ++ és --, typeof, sizeof +2. Pozitív és negatív operátorok, logikai és bináris tagadás, prefix ++ és --, $ a típusra és kifejezésre +3. Szorzás, maradékos és maradék nélküli osztás, modulus +4. Összeadás, kivonás +5. Bit-eltoló operátorok +6. Kisebb, nagyobb (vagy egyenlő), array(ptr, n), asif, is (típus equals check generic-re), islike(???) +7. Egyenlő és nem egyenlő operátorok +8. Logikai/Bináris ÉS +9. Logikai/Bináris XOR +10. Logikai/Bináris VAGY +11. Feltételes ÉS +12. Feltételes VAGY +13. Feltételes operátor (?:) +14. Értékadás + +Ez is esetleg király lehet (Shunting Yard Algorithm): + + https://www.youtube.com/watch?v=unh6aK8WMwM + +Meg ez is (basic block-os módszer): + + https://dengking.github.io/compiler-principle/Optimization/Basic-block/ + # OOP helyett: handler handler Vector { @@ -25,6 +62,7 @@ Ha megcsinálom a "strukturált formában tárolt programszöveges" ötletem, ak ^^Ezek "kezelik" a memóriákat és resource-okat RAII-módon, konstruktorral és destruktorral. Szerintem a struct-hoz képest is jobb szó. +Lesz továbbá tagged union is - lásd lejjebb - és interfészek (ami tagged union-á fordul le, hacsak ki nem optimizálja a fordító). # Memória és resource kezelés @@ -32,7 +70,7 @@ A lényeg ez (kb. "kötelező" RAII + extra szabályok dangling ref / pointer el - referenciákat lehet paraméterként megadni elvárt típusnak ÉS visszatérési értékként is metódusoknál - De referenciát eltárolni struct mezőben nem lehet - sima változóban félreteheted magadnak a stack-en ha nem akarod mindig kiírni az expression-t. -- Pointerekre meg van egy ownership modell, hogy a class-hoz / struct-hoz tartozik egy "resources" vagy "pointers" blokk... Valszeg csak külön syntax van erre, nem akarok ilyen hosszú szavakat kiírva látni, mert öli a produktivitást, hanem mint a C++ban pl van az inicializálós rész a konstruktornál, úgy a class-nál valami hasonló kis syntax erre... +- Pointerekre meg van egy ownership modell, hogy a handle-khez (class/ struct megfelelője) tartozik egy "resources" vagy "pointers" blokk... Valszeg csak külön syntax van erre, nem akarok ilyen hosszú szavakat kiírva látni, mert öli a produktivitást, hanem mint a C++ban pl van az inicializálós rész a konstruktornál, úgy a class-nál valami hasonló kis syntax erre... - Ugye pointereket vissza tudnak adni és el tudnak fogadni paraméterként @resource-nak és @release-nek jelölt függvények (mint pl. a malloc meg a free). - Az ilyen (így jelölt) függvényeket, csak konstruktorból és destruktorból tudod meghívni!!! - De persze a konstruktor beállíthat a pointerednek valami értéket - nem kell malloc-ból inicializálja meg! és ugye nem kell free-szerű dolgokat hívnia rá... @@ -59,6 +97,10 @@ A @unsafe-t lehet hogy class/struct pointer blokkjára is engedhetek rátenni, a Mondanom se kell: GC nyilván nincs így! +Ez meg szerintem fasság - nem jó érvek: + + https://dept-info.labri.fr/~strandh/Teaching/MTP/Common/Strandh-Tutorial/need-for-garbage-collection.html + ## Custom allokátorok / arénák A go-hoz újabban ajánlott "arénás" modell-t is lehet hogy valahogy alkalmazhatnánk - tehát hogy azzal oldjuk meg a custom allokátor kérdést... De szerintem egyszerűen úgy kell kialakítani a standard libet / kódokat, hogy template paraméter szerűen átvegye milyen @resource és @release függvényeket használ - tehát nem konkrétan malloc-ot, meg free-t mondjuk hanem csak azoknak megfelelő deklarációs függvényeket... Ezt C-ben is szoktam csinálni (pl. a kismap-ban van ilyen), de ugye ott függvény pointerekkel, amiket vagy eltárolok (de akkor runtime költsége van), vagy ha a perf fontos, akkor pl. a kismap esetén azt szoktam, hogy ezeket nem csak konstruálásnál, hanem minden hívásnál át kell adni ami használja - azért mert látom, hogy a compiler be tudja inline-olni, ha egy static inline force-inline stb. függvényt adok át neki function pointernek... De ez ugye "trükközgetés" feleslegesen és jó lenne, ha nyelvi elemként tudnánk ezt. Esetleg érdemes a hagyományos generik / template témától ezt különszedni? Nem biztos, lehet hogy azzal kezelendő. Cpp esetén template-ekkel szoktam ezt csinálni... @@ -69,7 +111,26 @@ Megj.: A "generational arenas" esetleg legyen támogatott? Ez egy olyan dolog, h # Párhuzamosság / Multithreading -Jó lenne TALÁN, pár experimental dolog: +Az "async"-ot szerintem felejtsük el... Én undorítónak tartom - a corutin támogatás is jobb annál... + +Esetleg lehetne egy direkt párhuzamosságra csinált "al-programnyelv" / DSL? + +Mint ez: + + https://github.com/HigherOrderCO/Bend + ++ Egy rust-os példa "könnyen elrontható aszinkronitásra: + + https://youtu.be/NaytZOiX3fs?t=780 + +(Van egy nem-async függvény, ami hibázhat (errors as values) és result object-et ad, de tudod, hogy nem hibázik... + ... ekkor ha async-á írod át mi lesz, ha előtte '_'-nek adtad értékül a result-ját? Mostantól a future-t fogod... + ... és a compiler nem szól, hogy elfelejtetted az await-et! Szóval kb. elcsesz neked mindent!!! Ilyesmi is baj... + ... nem csak az "elméleti" biztonság fontos egy nyelvben, hanem a pszichológiai is!!!) + +Esetleg a go-ból átemelni dolgokat? Meg Java JCIP-ből? - De ez lehet talán library szinten is / makró szinten is... + +De ha ezen nem is, akkor is jó lehetne TALÁN, pár experimental dolog: - Azonos logikai (és fizikai) CPU-ra confine-olt thread-ek, amiknél így nem kell lock-olás (greenthread szerű, esetleg a go arénákhoz hasonlóan scope-olt allokáció hogy hova spawnoljanak). - A go féle dolgok jók - kérdés, hogy java-szerűen ezek lib elemek legyenek, vagy mint a go-nál inkább nyelvi elemek. @@ -112,20 +173,74 @@ Legyen a kismaphoz hasonló - tehát (template) paraméterként kapnak a handle- ## Jobb pointerek -Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb. +Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb. Szerintem tömbre a default a restrict, de +a pointerre ki kell írni? -* Talán a tömbök külön típussá emelése ebben már eleve segít -* Mivel az ownerek kezelnek csak tömböket, ők adhatnk ki referenciákat és range-eket, de azok ugye nem alias-olnak be könnyen +* Talán a tömbök külön típussá emelése ebben már eleve segít - igen... legyenek továbbá mérettel is ellátva stb. lásd ott +* Mivel az ownerek kezelnek csak ptr-eket, ők adhatnk ki referenciákat és array-eket, de azok ugye nem alias-olnak be könnyen * A restrict kulcsszó támogatása a minimum - de talán kéne gondolkozni mit lehetne még kihozni, hogy jobb legyen! * Ugye mivel van külön "tömb" típusunk, ezért arra is lehetővé kéne tenni a restrict-et pl. paramétereknél, különben bakis lesz. +A tömböknél a restrict lehet ESETLEG a default - de ennek fényében kellene unrestrict kulcsszó rájuk! + +### NonOwningPointer - smartpointer + +Ezt szerintem érdemes C++ template-el előre megcsinálni! A lényeg az, hogy kreálni lehet a dolgot - tehát van egy factory-szerű, + + class Owner { + private: + std::vector vec = {1, 2, 3}; + + // Has a vector of pointers to children + // So when this go out of scope we can set child pointers backpointers to nulls (indicating non-living) + OwnPtrFactory fac; + + public: + // or access(..) etc. + // NownPtr has a pointer to the factory's own elem and on its move assign / move constructor calls updates facs child vec's + // pointer. Caller can ask the pointer if owner is already deleted or not. Maybe should be threadsafe? + NownPtr getMid() { + return fac.create(&vec[1]); + } + }; + +Ezt követően a használati ponton olyasmi történik, hogy: + + class User { + void f(..) { + ... + // Safe használat + NownPtr mid = owner.getMid(); + mid.if_exists([](int &i) { + ... i is accessible ... + }); + + // Unsafe használat (esetleg nálam @unsafe és konstruktor/destruktor esetben?) + int j = *mid + 40; + + // Unsage-nél is lehessen kérdezni + if(mid) { + int k = *mid; + } + + ... + } + } + +Ez tehát úgy látom C++ nyelven is lehetséges, egyedül mozgatáskor tűnik kicsit is "lassabbnak" meg ugye ez egy "fat" pointer, ami +2x pointernyi méretet használ fel... az egyik maga a mutatott terület címe, a másik a "factory"-ban lévő vektor megfelelő elemére +mutat rá - itt vigyázni kell: a vektor resize esetén invalidálódik ez! Kéne valami "stabil-vektor" ami blokkos és a resize-nál NEM +invalidálódik, de nem is egyesével, hanem blokkosával láncolt lista! + +Ezt esetleg nem is nyelvi elemként tennénk be, hanem library-ként a BASED-be is, az talán jobb? + ## Jobb tömbök Néhány dologra van szerintem szükségünk: -* (Pointer + méret / range) jellegű tömb történet, ahol a méret / range lekérhető a típusból ADA-szerűen -* Range subtype (ada szerűen) külön a típusrendszerben - hajlok arra, hogy 0..méret lehessen csak range viszont (praktikus: dyn) -* Dinamikus esetben, hívhasd az ilyen függvény paramétert sort(range(array, 42)) történettel (ahol array egy pointer, 42 méret). +* (Pointer + méret / range) jellegű tömb történet, ahol a méret / range lekérhető a típusból ADA-szerűen (.len) és az elemtípus is! +* Range subtype (ada szerűen) NEM kell külön a típusrendszerben, mert hajlok arra, hogy 0..méret lehessen csak (praktikus: dyn) +* Dinamikus esetben, hívhasd az ilyen függvény paramétert sort(array(ptr, 42)) történettel (ahol ptr egy pointer, 42 méret). A fordító két függvényt is generál ilyen függvényekből. A típusrendszer szerint azt a függvényt hívjuk meg, amelyik gyorsabb! Igazából ha fordítási időben ismert méretű tömbbel hívjuk meg, akkor a "sima" függvény hívódik, kivéve, ha ez valami linkelt lib, @@ -135,12 +250,14 @@ meg kellene oldani... A lényeg, hogy a "tömb" típus az pointer + hossz minden esetben, de ahol tudja, a fordító kioptimalizálja a történetet! Ha valami mást akar az ember, például performancia okokból, akkor simán adjon át egy pointert (és mondjuk null termináns). -Ha a "range" második paramétere nem fordítási idejű konstans szám, akkor ott is a dinamikus függvényt hívjuk majd meg. +Ha az "array" második paramétere nem fordítási idejű konstans szám, akkor ott is a dinamikus függvényt hívjuk majd meg. -A range, mint altípus megadás lehetővé teszi, hogy dolgok "osztozzanak" a méreten - pl. ECS egy játékmotorban, stb. -Emellett természetesen a standard library-ban kell "vector" típus és az tudjon range-t is adni neked. +A range, mint altípus megadás lehetővé tenné, hogy dolgok "osztozzanak" a méreten - pl. ECS egy játékmotorban, stb. +De mivel nem tól-ig, vagyis ADA-szerű range-jeink vannak, simán csak méret, ezért ha ilyet akarsz konstans számként tárolod. -Szerintem az így kapott kód "már kellően biztonságos" általában. +Emellett természetesen a standard library-ban kell "vector" típus és az tudjon array-t is adni neked. + +Szerintem az így kapott kód "már kellően biztonságos" általában. A vektorra debug build esetén legyen range check kötelezően! Lásd még: @@ -153,7 +270,7 @@ ui.: Ha nem lesz operátor overload, akkor szerintem legyen több dimenziós tö Erről még nem vagyok meggyőzve, de néha hasznosnak tűnnek: * begin és end klózok, melyek több switch-et körbefognak és mindegyikük előtt és után végrehajtódnak -* unchecked switch - ahol sose generálódik "mi történik, ha nincs olyan case" code path / jmp. Erre viszont kéne compile check is! +* checked switch - ahol sose generálódik "mi történik, ha nincs olyan case" code path / jmp, de compile time check: mindent kezelsz? Az előbbi egy kis usability feature és ugye a jump táblás megoldáson annyit változtat, hogy inline-ol oda közös részeket, mintha azok egy függvénybe forceinline lennének kitéve: tehát igazából C kódot tudunk generálni, ami ezt tudja. @@ -163,10 +280,76 @@ match statement-jéhez erre, hogy ne hagyjál ki valami esetet, akkor az pont a C kódot is a fordítónkkal, de akkor nem lesz "gyorsabb" a generált kód - saját fordítóval elvileg lehet gyorsabb is majd, mert ugyebár így tényleg teljesen jump tábla fog keletkezni. +Több-dimenziós switch: + + // checked meand all cases must be handled, unchecked means not all! + unchecked switch(shiftState, button) { + case (true, 'a'): + run_left(); break; + case (true, 'w'): + run_up(); break; + case (true, 's'): + run_down(); break; + case (true, 'd'): + run_right(); break; + case (false, 'a'): + walk_left(); break; + case (false, 'w'): + walk_up(); break; + case (false, 's'): + walk_down(); break; + case (false, 'd'): + walk_right(); break; + } + +^^Viszont szerintem ezt talán feláldozhatjuk a "nem kell break-t kiírni, de lehet a case mögött több esetet sorolni". + +NEM! Ne áldozzuk fel, mert akár lehet úgy is, hogy a ':' helyett más jelezze az összevont eseteket: + + case 'A'> + case 'B': + ... + case 'C': + ++c; + fallthrough; + case 'D': + ... + +TL;DR: nem lesz break; statement, de lessz fallthrough; statment! +Megj.: Ha nem lesz break; - akkor viszont kell szerintem "nop;" is - tehát kell parancs a semmittevésre, hogy üres case lehessen! + +## Ciklus break és continue helyett + goto kérdéskör + +Legyenek ezek: enditer; és nextiter; + +Esetleg: Simán válasszuk azt, hogy csak goto lesz és csőváz? +Mindenesetre a GOTO-ból legyen számított goto is (ez gcc / clang extension). + +Miért? + +* Hát a continue-t sose volt könnnyű érteni (a szó alapján) +* A break;-t nem célszerű használni, mert a switch-ből kikerül és így aki ott "reflexből" beírná, az break-elne a kinti ciklusából! + +## Visszatérési értékek, több visszatérési érték + +Legyen-e ilyen még a sima errors as values mellett? Mindenképp mellett, mert az error kezelés kikényszerítő plusz hatású, de +amúgy más tekintetben simán ilyenre fordulhatna a compileren belül, ha ez IS van! + + (int, int) swap(int a, int b) { + return b, a; + } + +Szerintem hasznos lehet mondjuk állapotgépeknél, a tagged enum-okkal együtt, ahol az állapottól függően a tagged enum a következő +állapotot ÉS mondjuk lexer esetén a nemterminális token-t is visszaadhatjuk (ha lett). Ezzel megspórolható egy out param! + +Ha lesz korutin, ott a "hagyományos" értelemben kéne a yield;-et használni szerintem. + ## Pattern matching Ez jó kérdés, hogy legyen-e. A rust-os match statement-nek azért vannak előnyei. Megfontolandó. +^^Szerintem a "checked switch" jellegű dolgok első körben elegek lesznek? Sőt a checked legyen szerintem a default! + ## Öröklődés, polimorfizmus - Nem lesz altípusos polimorfizmus. @@ -189,8 +372,28 @@ Például: }; // Megj.: Elfogad A a[42]; jellegű tömböt! - void sort(int-like elements[]) { - ... + void bubblesort(int-like arr[]) { + int n = arr.len; + int i, j; + bool swapped; + for (i = 0; i < n - 1; i++) { + swapped = false; + for (j = 0; j < n - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + // Swaps full handles (or when simple ints, just ints) + $arr.etyp tmp = $arr[j]; // Can access 'real' type + $arr[j] = $arr[j + 1]; // can operate on 'real' type and this works also for + $arr[j + 1] = tmp; // move, copy, functions (ad-hoc polymorphism), etc. + + swapped = true; + } + } + + // If no two elements were swapped by inner loop, + // then break + if (swapped == false) + break; + } } * Vegyük észre, hogy ez csakis fordítási idejű polimorfizmus ebben a formában! Ki kell tudnom számítani az offsetet! @@ -206,6 +409,187 @@ az egész handler természetesen azon a ponton! Megj.: Több ilyenje is lehet egy handle-nek! Nyugodtan lehet `like string name;` is itt! Megj.: Szerintem beleférhet még: `like readable int get() {..}` és/vagy esetleg `*like int get() {.. return pointer ..} ? +** FONTOS ** + +Még fontos azt is látni, hogy ilyen "int-like" vagy "MyHandleType-like" típusok csak deklarációkban lehetnek, a sima like kulcsszó +pedig csak definícióban / implementációnál! Tehát NEM lehet csinálni int-like arr[]; tömböt - csak mondjuk A arr[]; tömböt, de ez +utóbbit viszont át lehet adni paraméterként a sort(..) függvénynek így! + +## Tagged union + + tagged union AB { + int szam: + void triple() { this->szam *= 3; } + int integral() { return this->szam; } + void add(int other) {...} + float lebego: + void triple() { this->lebego *= 3; } + void add(float other) {...} + double legego2: + void triple() { this->lebego2 *= 3; } // kotelezo azonos szignaturaval ismetelni, amire nincs default! + string nevptr: + void triple() { this->nevptr = this->nevptr + this->nevptr + this->nevptr; } + default: + // Statics only work as defaults + static niniple(AB &a) { a.triple(); a.triple(); } + // Can add default implementation switch-cased with either checked or unchecked switch-es + int integral() { + // + unchecked switch(this->tag) { + case lebego: + ... + case lebego2: + ... + } + } + } + + AB ab; + ab.szam = 42; + assert(ab.tag is int); + ab.nevptr = "kula"; + assert(ab.tag is string); + +Másik lehetőség (esetleg a kettő párhuzamosan is lehet?): + + tagged union DynamicNumber { + int i; + float f; + + string to_string(string s) { + switch(this->tag) { + case int: + ... + case float: + ... + } + } + + static DynamicNumber from(string s) { + // ... + } + } + +Talán jobb volna, ha csak ez a "második" forma létezne? Lásd még tagged enum esete? + +Harmadik - legjobb? - lehetőség (ha tagged union-ban vagyunk, nem kell a switch-et kiírni): + + tagged union DynamicNumber { + int i; + float f; + + string to_string(string s) { + int: + ... + float: + ... + } + + void add(DynamicNumber &other) { + int: + ... + float: + ... + } + + static DynamicNumber from(string s) { + // ... + } + } + +Negyedik lehetőség - második + interfészes ötlet, ami talán még jobb: + +interface IGameObject { + spawn(float x, float y); + update(float gametime); +} + +handle Player implements IGameObject { + spawn(float x, float y) { ... } + update(float gametime) { ... } +} +// ... Zombie...Boss... + +// The "implements" clause is optional here +tagged union GameObject implements IGameObject{ +public: + // All of them need to implement the interface and calls get delegated to the proper one! + Player p; + Zombie z; + Boss b; + + // One can add methods as-is + string to_string(string s) { + switch(this->tag) { + case Player: + return "A jatekos"; + default: + return "Egy ellenseg"; + } + } +protected: + static void testAll() { + ... + } +} + +^^Ez a harmadik változat abban is jó, hogy könnyebben játszik össze a public-private kulcsszókkal is! +^^Mellesleg így kikerülhető, hogy default-olhassák (mert itt szerintem nem szabadna hagyni). + +Ahogy látható, ez a sima "union" kulcsszó egy "változata", amikor is a méret az a méretek maximuma + tagméret. +A tag alapján lehet szépen switch-elni rajta - de a dolog támogat egyfajta "polimorfizmust" is: Lehetnek az adott mezők után így +kettőspontos módszerrel (és beljebb identálni illik ilyenkor) az ahhoz köthető függvények - de ez nem kötelező! + +Ilyenkor egy "triple()"-t hívni rá, az helyben "legenerálja" a switch-case-t és a megfelelő hívódik meg! + +FONTOS: Talán érdemes volna kötelezővé tenni, hogy minden "esetnek" legyen meg minden implementációja, vagy hogy kettőspont nélkül +leírt függvény esetén neked kelljen magadnak switch-case-elned a történetet (és az fusson le). Ezzel egy adott függvénynek vagy +minden esetre kell legyen definíciója, vagy az únió eseteitől függetlenül álljon ott - de runtime error-t nem szeretnék! + +## Interfészek + +Zsolt vetette fel, hogy neki azért öröm töltené el a szívét, ha lehetne egy "kicsit OO style" azért annyiban, hogy egy tömbben tud +keverni különböző típusú dolgokat, amiknek közös az interfésze és kapni ilyeneket függvény paraméterként... + +Ez valamilyen szinten jogos elvárás és az únió részben tudja is ezt, mert csinálhat az ember egy tagged union-t amiben ő maga +sorolja fel így a típusait amik a tömbben lesznek és a fun() függvényeikben simán this->fun() átírányít / delegál akkor. + +No de! Ennél tudunk jobbat is! Mert ez nem kezeli azt az esetet, ha egy library-t akarok írni és ezt forrással együtt kiadni! +Ugyanis olyankor a lib írójaként én megszabom mely dolgokat veheti fel a tagged union - tehát előre kéne tudnom a use case-eket! +Ez nincs így! Ellenben a fordítót kihasználhatjuk arra, hogy generálja ezeket a tagged union-okat le, az alapján, hogy kik +implementálnak egy interfészt! + +Első változat: + + interface IStringifyable { + string stringify(); + } + + handle A implements IStringifyable { + ... + } + + handle A implements IStringifyable { + ... + } + + // Ez ilyenkor egy tömb, aminek az elemei igazából tagged union elemek, + // a build-ben meglévő összes azt implementáló dolog alapján... + void process(IStringifyable arr[]) { + ... + } + +Második változat: + + Lásd tagged union-oknál a "negyedik változat". Szerintem ez a legszebb!!! A tagged union tud implementálni interfészt és így + sokkal egzaktabban történik ugyanez "háttérben titkos tagged union generálás nélkül" - na így lett letisztult! + + + ha nem tömbről van szó, akkor a hívási ponton az egy lightweight generic támogatás / ad-hoc polimorfizmus is (kell-e vajon?) + +Az interfészekre lehet többszörös öröklődés (egymás közt is, meg itt is). + +FONTOS: A shared object-ek között NEM ajánlódnak ki ilyen jellegű típusok! Csak struct-ok! + ## Láthatóság * public @@ -266,6 +650,10 @@ annak azt mondom: az örökléshez meg nem kell semmi ilyesmi, szóval eddig pon - FONTOS: Legyen már lehetőség függvényt adni template paramnak (int-et is, meg típust is, de függvényt is please). - Az if constexpr (...) { .. } az tetszett a modern C++ból! +Érdekes írás: + + https://willcrichton.net/notes/the-coming-age-of-the-polyglot-programmer/ + ## Strukturált formában tárolt programszöveg (tm) fordítás Az első pontot nehezebb érteni, ha nem ismerjük a kontextust: Van ez az ötletem, ami forth-szerű, de faszerkezetben strukturált "compiler", ahol a forth szavaknak lehetnek "gyerekei" és ilyen blokkjai. A (...) blokk is csak egy block, mint a [..], vagy akár a {...}. Ugyanúgy forth szavakat definiálunk és meg tudjuk mondani, hogy a compiler az adott szóhoz érve mit csináljon. Tehát konkrétan scriptelni tudjuk a fordítót. Most nem mennék bele ebbe, de pár kiegészítéssel (papíron ez részletesen is megvan) kb. az összes "értelmes" nyelvet lehet így "parzolni". A fordításban csak egymásba pipe-olva állítjuk elő a következő és következő változatokat több pass-ban - és az utolsó pass ugye mondjuk fizikai fájlba ír, vagy interpreter esetén mondjuk végrehajt. Ezzel debuggolni is tök könnyű a "compiler/interpreter"-t vagy nézni hol mit optimalizál. @@ -274,12 +662,80 @@ Viszont ha ezt megcsinálom, akkor ezt az itt leírt nyelvet, ezzel implementál Megj.: A "néhány kiegészítés / részlet" azok olyasmik mint hogy nyilvántartjuk az indentálást (mind a hibajelzések, mind az ilyen python-szerű fos nyelvek miatt), kicsit trükközni kell a <..> jellegű dolgoknál (pl. generics, template), vagy épp hogy a "fordító scriptelése" azt is jelenti, hogy tudunk előre-olvasni vagy akár kézzel karakterenként olvasni és úgy mozogni az "input szöveg nyelén" ahogy Csörnyei mondaná Magyarosan a "handle"-t... Tehát ezzel tényleg nagyon testre szabható mi történik. Alapból stack-ed is van, mint a forth-nak, meg mellé vannak a fordító adatszerkezetei is (bár esetleg azokat csak akkor éred el, ha speciális jellegű szót definiálsz). +## Tagged enum / smartenum + +Ez hasonlít a taged union-hoz, de a union esetén a típus határozza meg a TAG-et. +Ebben az esetben a típus mindig azonos és valamelyik integer alaptípus (pl. int). + +Példa: + + tagged enum(int) FileError { + NOT_FOUND = 0: + const char *msg() { return "File not found!"; } + PERMISSION_ERROR = 1: + const char *msg() { return "Not suitable permissions!"; } + } + +Másik lehetséges alak: + + tagged enum(int) FileError { + NOT_FOUND = 0; + PERMISSION_ERROR = 1; + + const char *msg() { + switch(this->tag) { + case NOT_FOUND: + return "Not suitable permissions!"; + case PERMISSION_ERROR: + return "File not found!"; + } + } + } + +Lényegében a java smartenum-jához hasonló dolog, vagy ahhoz a smartenumhoz amit én csináltam az impexlib C++ kódbázisban! + +Megj.: Itt is talán a második formát kéne csak engdni? Lásd "tagged union"-nál lévő két alak közül a második? + +Megj.: Esetleg megfontolni miként segíti ez az állapotgépek használatát / parzer építést? Esetleg a 2D switch-el? + ## Exception és hibakezelés - Exception-ök nincsenek - Go és rust-hoz hasonló hibakezelés az szerintem jó ötlet, de egy-az-egyben egyik se tetszik ebből a kettőből. Jó ha van valami syntax sugar és az is, ha kötelezővé tudod tenni az error-t is visszaadó függvény hívásánál a hibakezelést, vagy tovább propagálást. - Stack unwinding, meg ilyesmi sincs így: egyszerűen syntax sugar van, hogy fordításkor kikényszerítheted a hiba lekezelését ha valaki hív téged. Az egyetlen kérdés, hogy mi történik a tagged union-os esetben ha polimorfizmus van és más-más esetben lehet, vagy nem lehet hiba... hát a válasz az, hogy a függvény deklarációban benne kell legyen a hiba szóval a hiba maga itt nem polimorf (vagyis csak ugyan ennyire, de nem lehet olyan, hogy valamely tagged union variáns olyan hibát dob és úgy, ahogy az nincs interfészben jelölve). +A legjobb egy Zig-szerű megoldás lenne - jelenleg ezt mondanám a választásomnak: + + // TODO: Just example, not real code + tagged enum(int) FileError { + NOT_FOUND = 0: + const char *msg() { return "File not found!"; } + PERMISSION_ERROR = 1: + const char *msg() { return "Not suitable permissions!"; } + } + + FileHandle open_file(const char *path) onfail FileError { + ... + fail FileError.NOT_FOUND; // error handlers can also fail again: both their own errors or "fail err;" to re-fail. + ... + } + + void testcode() { + // onfail clause is mandatory if function declaration has an onfail - unlike with exceptions + // Its "errors as values" philosophy and implemented via struct returns / double return values. + FileHandle h = open_file("help.txt") onfail(err) { + // Must handle all of the cases (or have a "default"?) + NOT_FOUND: + // Can chain, and have visibility for variables in the main expression (here: h) + h = open_file("secondary_help.txt") onfail(err2) { + default: + printf("help.txt not found and secondary_help.txt errs with: %s\n", err2.msg()); + } + PERMISSION_ERROR: + puts(err.msg()); + } + } + Alternatíva: - Volt az a gondolatom az error-stream hibakezelésről, ahol error stream-eket lehet definiálni @@ -313,6 +769,28 @@ Talán a megoldás valami olyasmi, hogy ne teljesen seamless integrációt csin - runtime type info: szerintem nem kéne ilyen... érdekes, de a JonBlow-nak kell a Jai-ba és Gamedev-re is sokan sírnak érte? - reflection: fú szerintem nem nagyon kéne - maximum compile time kiszámítható pár dolog... +## Típuskonverziók + +* Szerintem ne bonyolítsuk és legyen c-style cast-olás, csak sokkal erősebben típusosan és ne legyenek implicit konverziók. +* Mellette legyen egy "asif" kulcsszó, amit reinterpret_cast műveletként használhatunk. + +Lásd precedenciánál. + +Szerintem ez elég tömör, de mindent leír: + + int a = 0x80000000; + float f = a asif float; + int b = int(f); + +Azért nem simán "as" a kulcsszó a reinterpretálásra, mert szerintem ez jobban kifejezi, +de még mindig rövidebb, mint a "reinterpret", de nem gondol az ember mást róla, mint ami! + +Az alaptípusokra pedig az, hogy "konstruktor-szerűen" működnek az szerintem egyébként +is konzisztensebb, mintha elé írom C-szerűen zárójelben a cast-olást... + +Mivel a nyelvben az (alap)típus mögött nem jön zárójel (konstruktor hívást jelent), ezért +ez minden további nélkül megoldható a fordítóban és mentálisan tényleg konzisztensebb... + ## AOP - Szerintem pár dolog azért hasznos lenne az aspectj-ből átvéve. Úgy, hogy nem lesz reflection, azért elég sok mindent pótolna ez!