lot of progress in language design - tagged unions and interfaces starts to look well, as well as error handling and many minor things too
This commit is contained in:
parent
7b1e0e39b4
commit
1ce293a4b1
508
BASED.md
508
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<T> {
|
||||
@ -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<int> 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<int> 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<int> 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!
|
||||
|
Loading…
x
Reference in New Issue
Block a user