First drafts - it was long existing, but committed to git
This commit is contained in:
commit
7b1e0e39b4
362
BASED.md
Normal file
362
BASED.md
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
# A "based" programnyelvről
|
||||||
|
|
||||||
|
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á.
|
||||||
|
|
||||||
|
# Elnevezések
|
||||||
|
|
||||||
|
- *.basd
|
||||||
|
- ./based build
|
||||||
|
- "Bézd"
|
||||||
|
- Based Advanced Simple Effective Devlang
|
||||||
|
- - - - -
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# OOP helyett: handler
|
||||||
|
|
||||||
|
handler Vector<T> {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
^^Ezek "kezelik" a memóriákat és resource-okat RAII-módon, konstruktorral és destruktorral.
|
||||||
|
Szerintem a struct-hoz képest is jobb szó.
|
||||||
|
|
||||||
|
|
||||||
|
# Memória és resource kezelés
|
||||||
|
|
||||||
|
A lényeg ez (kb. "kötelező" RAII + extra szabályok dangling ref / pointer ellen):
|
||||||
|
|
||||||
|
- 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...
|
||||||
|
- 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á...
|
||||||
|
- Meg persze a @resource nem kötelező, hogy pointer-t adjon vissza - mert adhat valami handle-t is mondjuk. Tehát ennek a "pointeres" blokknak vagy lehetnek nem pointeres változói, ami mondjuk egy integer csak, de úgy jobb, ha minden resource-t szerintem oda írsz - alternatíva, hogy tényleg csak a pointereket jelöljük külön is és akkor sima mezőnek ad értéket a konstruktorban a @resource-os függvény ami mondjuk egy adatbázis handle-t ad vissza valami hívásból és tárol el.... Lehet hogy tisztább ez így, csak így a resource handle-k nem egy helyen vannak, hanem félig a pointerek - félig a sima adatok közt. Valszeg érdemes pointer blokkot csinálni ami tényleg csak pointereket tartalmaz és ha nem használod - hát nem használod.... Egyre inkább hajlok az utóbbira, mert mégis tisztábbnak hangzik...
|
||||||
|
- Nyilván ezek mind privátok: de ha valaki referenciát ad paraméternek a saját típusodból egy metódusodban, az abban lévőket eléred mert van rá ugye visibility-d úgy. Meg esetleg ugye csinálhatsz friend jellegű dolgot, vagy "owner" class-t aki eléri a pointereid. Esetleg ha egymásba ágyazott class-okat csinálhatsz, akkor ez default bekerül friend-ként, hogy a class törzsében definiált másik class alapból a kintit tekinti ownernek... Ez az "owner class" jó ötletnek tűnik - el is neveztem protectornak - és így lehetnek protected pointerek - na azokat éri el az a speciális class / handler!
|
||||||
|
- A pointereknek adhatsz értéket, növelheted, csökkentheted, szorozhatod meg a faszomsetudja még mit akarsz vele. De csak a saját pointereidre van normális esetben láthatóságod...
|
||||||
|
- Alapból a pointer lehet null - alapból a referencia nem lehet null! Külön kell jelezni, ha lehet null egy visszaadott referencia, vagy paraméterben leírt ref - tehát ez a típus része. Mondjuk egy &ref? jellegű syntax, vagy valami ilyesmi.
|
||||||
|
|
||||||
|
^^ez szerintem majdnem mindent kezel, de nyilvánvalóan nem mindent és pont az a célom, hogy a rust-al ellentétben NE akarjak mindent safe-en tartani, csak minél többet...
|
||||||
|
|
||||||
|
asszem egyedül ezt nem kezelem így:
|
||||||
|
|
||||||
|
T& genyoka() {
|
||||||
|
vector<T> tmp;
|
||||||
|
tmp.pushback(T());
|
||||||
|
return &tmp[0];
|
||||||
|
} // obviously dangling reference
|
||||||
|
|
||||||
|
^^de mivel a referenciákat kvázi "nem tárolhatod el" ezért az ilyen problémák baromira lokálisak maradnak szerintem - nem szétszóródnak a kódban.
|
||||||
|
|
||||||
|
Természetesen ha @unsafe-nek jelölöd a függvényeid, akkor eltekinthetsz ezektől - olyankor mondjuk hozzáférsz mások pointer adattagjaihoz, használhatsz és adhatsz vissza pointer-eket és hasonlók. De ha a hívó maga nem unsafe, akkor nem tudja eltárolni a pointered, mert ugye nem tud rá változót csinálni (ha van valami var/auto jellegű típuskikövetkeztetés, akkor ott le kell állítani, ha nincs akkor eleve nem probléma, mert le se tudja írni az illető). Ugyebár természetesen az ilyen módon jelölt függvények továbbá mindenféle módon jelölt függvényt simán meg tudnak hírni - tehát pl. malloc-ot, vagy egy adatbázis kapcsolat létrehozást / elengedést is.
|
||||||
|
|
||||||
|
A @unsafe-t lehet hogy class/struct pointer blokkjára is engedhetek rátenni, ami azt jelentené, hogy az ő pointereit más is látja, esetleg egész class-ra / struct-ra, amelybe lévő dolgok így öröklik lefele
|
||||||
|
|
||||||
|
Mondanom se kell: GC nyilván nincs így!
|
||||||
|
|
||||||
|
## 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...
|
||||||
|
|
||||||
|
A C++ esetén ugye probléma, hogy van "new", de mi eleve nem használunk ilyen dolgot.... A "new"-t amúgy is át kéne úgy nevezni, hogy "allocate-on-heap-while-being-slow-and-randomly-place-in-memory"... Mert nem kéne, hogy össze legyen kötve az allokálás fogalma az "új dolog létrehozásával" ugyebár... Ez csak az OOP miatti agymenés...
|
||||||
|
|
||||||
|
Megj.: A "generational arenas" esetleg legyen támogatott? Ez egy olyan dolog, hogy az elemekhez amit allokálsz, odakerül egy "int generation" mező prefixként. Az indexek ezt követően "fatpointer" szerűek (mondjuk 2x32bit, vagy akár 2x16) és a "generációt" is tárolják "optimistic locking" szerűen. Ha törlésre kerül egy elem, akkor a generációja (-1*) szorozva lesz: ebből látni, hogy nem ugyan az, mint amire még az indexem valid lenne, tehát már dead object. Ellenben ez a memória cella / terület újra kiadható (és nem kell hosszú free-list, mert elég párat tárolni és utána szkennelni (akár külön szálon szkennelni amikor kiadok a listáról - ez a szál nyilván thread pool-os jelleggel már létezik, csak pihen) és ilyenkor a területre beadható a ((-1*gen) + 1) új érték, ami miatt a generáció ugye megint csak nem az lesz, mint amit hivatkoznak! Ez valszeg nem kéne nyelvi elem legyen, de attól függ mennyire arénázunk.
|
||||||
|
|
||||||
|
# Párhuzamosság / Multithreading
|
||||||
|
|
||||||
|
Jó lenne 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.
|
||||||
|
- Lock-free programozáshoz legyen memory modell... Lehetőleg a C/Cpp-hez hasonló??
|
||||||
|
- Pub-sub primitívnél library szinten lehetne az ötletem amit balásznak meséltem a volatile (igen a C-s volatile!) változóról, azonos fizikai, de külön hyperthread processzoron lévő szálnál gyorsabb kommunikálásra??? Nem hiszem, hogy nyelvi elem kéne legyen, de esetleg standard lib.
|
||||||
|
|
||||||
|
De a minimálisabb követelmény / alapabb szett
|
||||||
|
|
||||||
|
Szerintem kell nyelvi szintű memory modell, az atomic jellegű dolgok és memory barrier-ek átvétele a C++ból kb. jó lesz és még
|
||||||
|
talán a volatile is maradhat a C-s jelentésében (vagyis nem java-szerűen). Emellett egy sima és egy read-write lock még jó cucc.
|
||||||
|
|
||||||
|
Jó kérdés, hogy akarunk-e valami SIMD történetet. Első körben legyen elérhető a C-re fordítás, majd ott bele gányolják ha kell.
|
||||||
|
|
||||||
|
Ilyen go-jellegű, gorutinos történet talán jó volna, esetleg pipeline és tisztán funkcionális esetre ilyen automatikus PP.
|
||||||
|
|
||||||
|
Low-prio... Őszinte leszek - igazából a memory model csak az "igazán" fontos, a többire lehet hogy elég egy pthread...
|
||||||
|
|
||||||
|
# Standard library
|
||||||
|
|
||||||
|
- Az "igazán" standard libnek szerintem nem kell "prefix" meg "namespace".
|
||||||
|
- Legyen string típus
|
||||||
|
- stl-szerű dolgok, de pl. lehetne numberizer-alapú a sort algók interfésze (Magyarsort, meg kicsit átgondoltabb, stb. stb.)
|
||||||
|
- Felmerül, hogy akarunk-e Jai-szerű opcionális desktop standard libet: ablakozás, alap grafika, stb.
|
||||||
|
- Felemrül, hogy akarunk-e az előző ponthoz hasonlóan opcionális web standard libet: pl. rest server, uWebsocket-el, clientside bindingok a dom-on mászkálásra, stb... Talán első körben nem kéne
|
||||||
|
|
||||||
|
## Memória a standard lib esetén
|
||||||
|
|
||||||
|
Legyen a kismaphoz hasonló - tehát (template) paraméterként kapnak a handle-k malloc és free függvényeket? Vagy van erre nyelvi cucc
|
||||||
|
|
||||||
|
# Filozófia
|
||||||
|
|
||||||
|
- Suckless - amennyire csak lehet legyünk minimalisták és egyszerűek!
|
||||||
|
- Lehetőleg ne millióféleképpen lehessen valamit megoldani...
|
||||||
|
- Ne akarjunk "tökéletességet", inkább praktikusságot. Pl. A borrow checker talán tökéletesebb, de bonyolultabb érteni.
|
||||||
|
- Minimális függőségek (pl. lehetőleg ne kelljen LLVM ehhez!)
|
||||||
|
|
||||||
|
Összességében "worse-is-better" jellegű filozófia atekintetben, hogy inkább legyen az implementáció egyszerű, mint mindenre kiterjedő - és a konzisztenciához képest is fontosabb ez, mellesleg az implementációs egyszerűség fontosabb az interfész egyszerűségénél is.
|
||||||
|
|
||||||
|
# Egyéb dolgok
|
||||||
|
|
||||||
|
## Jobb pointerek
|
||||||
|
|
||||||
|
Valahogy jó lenne elérni, hogy a default a "restrict" kulcsszóhoz legyen közelebb.
|
||||||
|
|
||||||
|
* 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
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
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,
|
||||||
|
mert abban az esetben természetesen a "valódi" függvény fog hívódni - ezt C-re fordításnál még nem látom át hogyan kéne, de majd
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Szerintem az így kapott kód "már kellően biztonságos" általában.
|
||||||
|
|
||||||
|
Lásd még:
|
||||||
|
|
||||||
|
https://youtu.be/MUISz2qA640?t=1980
|
||||||
|
|
||||||
|
ui.: Ha nem lesz operátor overload, akkor szerintem legyen több dimenziós tömb (nem a tömbök tömbje, hanem: matrix[15, 10, 42] = 5;
|
||||||
|
|
||||||
|
## Holy-C féle kiegészítések a switch statement-hez
|
||||||
|
|
||||||
|
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!
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
A második egyébként lehet hogy pont `checked switch` kéne legyen. Miért? Hiszen ha compile time ellenőrzés van - hasonlóan a rust
|
||||||
|
match statement-jéhez erre, hogy ne hagyjál ki valami esetet, akkor az pont a checked nem? Első körben ebből is generálhatunk
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Pattern matching
|
||||||
|
|
||||||
|
Ez jó kérdés, hogy legyen-e. A rust-os match statement-nek azért vannak előnyei. Megfontolandó.
|
||||||
|
|
||||||
|
## Öröklődés, polimorfizmus
|
||||||
|
|
||||||
|
- Nem lesz altípusos polimorfizmus.
|
||||||
|
- Lesz generic / template jellegű fordítási idejű polimorfizmus
|
||||||
|
- Lesz "tagged union" jellegű polimorfizmus (lásd rust enum, vagy pl. std::variant)
|
||||||
|
- A kompozíció könnyítésére lesznek "protector"-ok, vagyis védnök szerepek a handle-k között a protected mezők elérésére.
|
||||||
|
- El kéne gondolkozni a kompozíció típusrendszerbeli megjelenítésén: Lehessen-e "int-like" vagy "MyType-like" stb?
|
||||||
|
|
||||||
|
Lennének ugye a hagyományos típusok, olyankor ezek "konkrétumot" jelentenek. Tehát egy int az egy integer és nem más, sem pedig
|
||||||
|
nem valami dolog ami az "int-ből öröklődik". De ez igaz a konkrétság a nyelvben ugye így öröklődés hiányában igaz lenne a saját
|
||||||
|
handle típusokra is (a struct-ok okosabb neve itt a handle).
|
||||||
|
|
||||||
|
Ellenben megadhatjuk egy típusnak, hogy egy-egy mezője (annak típusa alapján) "legyen a mi típusunk más típusú 'nézete' ott"!
|
||||||
|
|
||||||
|
Például:
|
||||||
|
|
||||||
|
handle A {
|
||||||
|
like int key;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Megj.: Elfogad A a[42]; jellegű tömböt!
|
||||||
|
void sort(int-like elements[]) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
* Vegyük észre, hogy ez csakis fordítási idejű polimorfizmus ebben a formában! Ki kell tudnom számítani az offsetet!
|
||||||
|
* Azt is vegyük észre, hogy kell valami "operátor": Ugyanis akarok egy elemet írni - vagy csak a nézetét (default: nézete).
|
||||||
|
* Továbbá szerintem jó, ha le tudjuk kérdezni a "valódi típust" - mondjuk hogy deklarálhassam lokális tmp-nek a bubble sortban...
|
||||||
|
* Az eredmény: sokkal tisztább, kisebb algoritmusok "mintha csak számokon dolgoznék és nem adaton".
|
||||||
|
|
||||||
|
Fontos: Nincs auto-konverzió! Ha nem azt írom, hogy "int-like" dolgot várok, akkor csak int-et kaphatok! Ez igaz akkor is, ha egy
|
||||||
|
RovarAdat dolgot várok és egy BogarAdatban lenne egy `like RovarAdat r;` akkor RovarAdat paraméterré nem válik a BogarAdat, csak
|
||||||
|
akkor, ha direkt kiírom, hogy "BogarAdat-like" az a bizonyos paraméter! Továbbá ha nem referencia / nem pointer, akkor másolódik
|
||||||
|
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 ..} ?
|
||||||
|
|
||||||
|
## Láthatóság
|
||||||
|
|
||||||
|
* public
|
||||||
|
* readable
|
||||||
|
* resource
|
||||||
|
* protected (*)
|
||||||
|
* readable protected (*)
|
||||||
|
* protected resource (*)
|
||||||
|
* readable protected resource (*)
|
||||||
|
* private
|
||||||
|
|
||||||
|
Ezekből több blokk is lehet (C++ szerűen) és többször is szerepelhetnek, ezáltal lehetővé tesszük a layout megadást!
|
||||||
|
|
||||||
|
A public gondolom elsőre is érthető s a default. A readable az lényegében egy segítő ficsőr, hogy ne legyen annyi "getter":
|
||||||
|
Ugyanis ez azt adja, hogy mások kívülről olvashatják, de én belül még írhatom.
|
||||||
|
|
||||||
|
A resource csak a konstruktorban és destruktorban érhető el. Tehát az "életciklus" alatt readonly ("ezt kezeli a handler").
|
||||||
|
|
||||||
|
A protected itt érdekes, mert nincs öröklődés, ellenben a handle-k megadhatják kiket protektálnak (kiknek védnökei):
|
||||||
|
|
||||||
|
handle A {
|
||||||
|
protected:
|
||||||
|
int a = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
handle B {
|
||||||
|
protected:
|
||||||
|
int b = 42;
|
||||||
|
};
|
||||||
|
|
||||||
|
handle C protects A, B {
|
||||||
|
static work(A &a, B &b) {
|
||||||
|
print(a.a + b.b); // possible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Nem inheritál, csak simán ad láthatóságot - főleg akkor hasznos, ha van B adattagod, vagy egy vektorod rá, vagy dolgozol vele.
|
||||||
|
Ez segít kicsit több kontrollt elérni kompozíciónál. Esetleg ez a típus része kéne legyen?
|
||||||
|
|
||||||
|
handle C {
|
||||||
|
static work(protectee A &a, protectee B &b) {
|
||||||
|
print(a.a + b.b); // possible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Így egy "readable protected" kombó is járja akkor már ami kifele olvasható, de csak protectoroknak írható. Hasonlóan jön még
|
||||||
|
a "protected resource" és a "readable protected resource" is - de ezek igazából kombinációk / modifier-ek ilyen módon.
|
||||||
|
|
||||||
|
Ha valaki erre azt mondja - ez mekkora hülyeség! A C++ legalább a friend-nél úgy csinálja, hogy a class meg kell mondja a barátait, nem csak úgy "jelentkeznek" a barátai a semmiből.....
|
||||||
|
annak azt mondom: az örökléshez meg nem kell semmi ilyesmi, szóval eddig pontosan ez volt örökléssel, hogy aki csak szeretett volna ilyet, odaírta, hogy örököl tőled és ugye hozzáfért a protectedhez....
|
||||||
|
|
||||||
|
## Metaprogramozás
|
||||||
|
|
||||||
|
- Az implementációs közegből kifolyóan, a compiler scriptelésével - esetleg itt-ott erre valami syntax sugar, hogy ne legyen bonyi.
|
||||||
|
- A zig comptime-ra érdemes még ránézni és valami hasonlót esetleg csinálni? Azt sokan szeretik és produktívabb, mint a rust makrók.
|
||||||
|
- Valamilyen szintű generic: lehetőleg nem ducktype-os, hanem a template-ekhez közelebbi módon okosabb, de nem turing teljes
|
||||||
|
- Igazából a fent leírt "trükközés" az "AKA" kulcsszóval és "*AKA" kulcsszóval!
|
||||||
|
- 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!
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
Viszont ha ezt megcsinálom, akkor ezt az itt leírt nyelvet, ezzel implementálva alapból van metaprogramozásunk - méghozzá mindennél erősebb. De ugye nem feltétlenül kényelmes is... Pl. a jai, vagy a zig comptime lehet hogy sokkal kényelemsebb.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
Alternatíva:
|
||||||
|
|
||||||
|
- Volt az a gondolatom az error-stream hibakezelésről, ahol error stream-eket lehet definiálni
|
||||||
|
- Ez kicsit egybemossa / együtt kezeli a logolást és hibakezelést. Konkrétan az error-ok és malfunction-ök ilyen stream-be kerülnek, ahonnal popp-olhatjuk őket - vagy ignorálhatjuk is. Ha hibát ignorálunk, akkor ebből alapesetben leállás lesz, ha malfunction-t, akkor viszont logolódik. De ignorálás helyett tehetünk is valamit - ami lehet az is, hogy kézzel leignoráljuk pl., vagy akár a sorban hagyjuk. Ez egyfajta globális hibakezelés, csak a getlasterror-jellegű dolgok helyett nem felejtődnek el a hibák - hacsak úgy nem konfiguráljuk a dolgokat. Ha "úgy" konfiguráljuk, akkor thread-local szemantikával globálisak csak - tehát nem úgy, mint a C-s esetben a nagyon-nagyon globális hibák.
|
||||||
|
|
||||||
|
Ezt elég barokkosan kidolgoztam, de az volt a bajom vele, hogy túl összetett és talán nehezen érthető. Az, hogy a hibák simán értékek, mint go-ban, vagy rust-ban az sokkal egyszerűbb - és ennek a nyelvnek az egyik előnye az egyszerűség lenne ugyebár pont!
|
||||||
|
|
||||||
|
## Szintaxis
|
||||||
|
|
||||||
|
Jobb szeretném, ha a C-hez közelebb állna a syntax, mint pl. a zig, vagy rust csinálja. Sőt az optimális az lenne, ha bármely C kód fordulna is - amennyiben egy @unsafe-t ráteszünk az adott függvényre. Ez persze nem strict cél, hanem hogy "általában így legyen" mondjuk az már jó lenne. Tehát nem feltétlen lenne jó mindent is implementálni a C-ből, de pár alap dologban legyünk hasonlatosak szerintem.
|
||||||
|
|
||||||
|
## Visszafelé kompatibilitás
|
||||||
|
|
||||||
|
Igazából nem cél a visszafelé kompatibilitás. Nem is nagyon lehet megmondani, hogy mi lenne a nyelv "őse" ami felé kompatibilis kéne legyen nyelvi és szintatktikai szemmel nézve...
|
||||||
|
|
||||||
|
Ami viszont jó lenne, ha egyből tudnánk fordítani C/C++ kódot a zig-hez hasonlóan. Ez a zig-nek azért brutális előnye! Viszont vannak ezzel kapcsolatban azért komoly nehézségek, ezért lehet hogy sokkal járhatóbb út, ha ezt a lépésenkénti átállást inkább úgy támogatjuk, hogy az egyik referencia implementációja a fordítónak C/C++ kódot fordít! Azért így írom, hogy C "slash" C++, mert szép dolog C-re fordulni (és onnantól minden mikrovezérlőn is elfutni, meg mindenhol, ahol van C), de a C++-ra fordulásnak akkor van előnye, ha C++ interop-ot szeretnénk és például hívni akarunk onnan osztályokhoz tartozó metódusokat, vagy hasonlókat.
|
||||||
|
|
||||||
|
Az itt a probléma, hogy ha megengedjük, hogy C++ kódokat hívhassunk (azáltal, hogy C++ra fordulni is tudunk), akkor vajon a nem C++-ra forduló fordító backend ezt hogyan fogja kivitelezni? Az extern "C" jellegű dolognál nincs annyira ez a probléma, de praktikusan ezt megoldani mégis elég hasznosnak tűnik, mert akkor legacy kódot tudnak erre a nyelvre emelni a zig-hez hasonlóan.
|
||||||
|
|
||||||
|
Talán a megoldás valami olyasmi, hogy ne teljesen seamless integrációt csináljunk, hanem "csak majdnem".
|
||||||
|
|
||||||
|
^^Azért a nyelv saját magával legyen már visszafelé kompatibilis majd... szóval nem akarok python-szerűt csinálni majd...
|
||||||
|
|
||||||
|
## Típusrendszer
|
||||||
|
|
||||||
|
- Alapjában véve erősebben lenne típusos, mint a C/C++. Tehát pl. nem engedünk int-ből double-be csak úgy adatot tenni meg ilyenek, kevesebb lenne az ilyen mágia is...
|
||||||
|
- Jó kérdés, hogy legyen-e típus-kikövetkeztetés, meg ilyen modern cuccok amit a "nyelvek típusrendszere" kurzuson tanultam még egyetemen és sok modern nyelvben van.
|
||||||
|
- A referenciánál az, hogy lehet-e null az a típus része!
|
||||||
|
- Öröklődés alapú polimorfizmus: nincs és nem is akarok!
|
||||||
|
- Szerintem NEM duck-typing-al kéne generic-et csinálni, vagy template-et...
|
||||||
|
- 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...
|
||||||
|
|
||||||
|
## 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!
|
||||||
|
- Igazából az erős metaprogramozás miatt ez szerintem megvalósítható ilyen @Before és @After dolgokkal meg @MyAOP jelölőkkel.
|
||||||
|
- Megj.: Talán egyszerűen a @-os szavak koncenció szerint dolgozhatnak a mögöttük lévő cuccoson / talán ez nem is külön effort,
|
||||||
|
de úgy érzem valahogy jobb lenne ezt külön nyelvi szintre emelni? Tényleg nem biztos, mert elvileg ez megírható anélkül..
|
||||||
|
|
||||||
|
## Plugin / modul architektúra
|
||||||
|
|
||||||
|
- Lehet hogy jó volna nyelvi szinten rendesen támogatni? Vagy akár csak library szinten? Lásd videót ahol TsodingDaily hot code replace-t csinál C-ben. A rákövetkező videó is jó, ahol X-makrókkal csinálja meg hogy olvashatóbb legyen (amikor fordítunk dolgokat akkor is jó volna ezt is ismerni - addigra valszeg elfelejtem és nézhetem újra).
|
||||||
|
- Fordítási idejű plugin/modulozás viszont tök hasznos volna! Az talán nyelvi szinten is!
|
||||||
|
- importálás talán java szerűen, csak annál is egyszerűbben. Majd megnézem a go hogyan csinálja pl. A lényeg, hogy include helyett jó lenne ha a fordító tudná hogy vannak modulok és azok mit exportálnak, onnan mit importálunk. A C/C++ modulokat is meg kéne nézzem hogyan működnek - főleg ha kéne az interop (vagy akár az erre fordító fordító backkend..)
|
||||||
|
- Fordítási idejű dependency injection is lehetne.
|
||||||
|
|
||||||
|
Amit nem kéne: Nem kéne, hogy az egész világ annyira dinamikus és szálkezelős legyen, mint az OSGi-al java-ban. De pl. onnan a product-modul-komponens felosztást jó lenne megcsinálni. A modul-t és namespace-t összekötném (mint ahogy java-ban OSGi modulokkal össze is kötöttem jellemzően?) - esetleg a runtime esetet úgy kezelve, hogy a namespace megjelölhető modulnak, de így ugye lehetnek sub-namespace-jei.
|
||||||
|
|
||||||
|
Azt, hogy hol keresse a kódot jó kérdés hogy java-szerűen akarom-e, vagy C/C++ szerűen... tehát hogy a namespace szabályok írják-e le, vagy sem. Mindkettőnek van előnye ugyebár... és hátránya is...
|
||||||
|
|
||||||
|
## Operator (és egyéb) overloading
|
||||||
|
|
||||||
|
- Magam is megosztott vagyok, hogy legyen-e. Valamennyire azért kívánatos pl. egy vektor típusnál hogy simán tömb-szerűen használható.
|
||||||
|
- Azoknak is igaza van, hogy nehezebb követni mi történik: mekkora a költsége egy műveletnek...
|
||||||
|
- Igazából a múltkor a "dinamikus programozás és algebrai csigamátrix optimalizálás" videómban nagyon kényelmes volt! De talán külön kéne "rendes" 2D tömb nyelvi (vagy meta?) szinten és csőváz? Vagy esetleg ha már overload-al van csinálva, akkor megoldhatnánk, hogy matrix[x][y] legyen? Egyébként szerintem ez C++ esetén is megoldható... vissza kellet volna adnom egy objektumot az offset értékkel és az eredeti tömb pointerrel - és ezen objektumnak lett volna szintén operátor overload-ja, ami a végső számítást végzi...
|
||||||
|
|
||||||
|
## Fordítás
|
||||||
|
|
||||||
|
Mint említettük korábban a "strukturált formában tárolt programszöveges" ötlettel lenne ehhez referencia implementáció - sőt ha metaprogramozásnak azt tartjuk meg, akkor valószínű, hogy más implementációk is azt kell használják - ez egy érdekes kérdést vet fel, mert lehet hogy gyorsabb kód fordítás lenne elérhető, ha nem azzal metaprogramoznánk, hanem csak arra átírnánk a kódot - tehát ha kikötjük, hogy nem lehet ilyen metaprogramozás (forth-szerű szó definiálás), hanem mi magunk saját metaprogramozást alakítunk ki, amit arra fordítunk az első lépésben le... Ez lehetővé tenné, hogy mások, más módon is implementálhassák a nyelvet - ami vagy jó, vagy nem jó...
|
||||||
|
|
||||||
|
Én arra gondoltam, hogy hasznos lenne olyan, ami (emberileg olvasható) C vagy C++ kódra fordít bináris helyett - de emellé egy "rendes" fordítót is érdemes volna írni. Először is, ezzel nagyon jó optimalizációkat kapunk "ingyen", másodszor az interop-nak nagyon segít.
|
||||||
|
|
||||||
|
Viszont ugye a saját megoldás is nagyon fontos, de egyben ott a strukturált formás ötlet, ami erre lehetőséget is ad. Milyen target-eket kéne viszont supportálni? Szerintem a legjobb az volna, ha direkt webassembly (wasm) targetünk lenne! Emellé esetleg még egy x86-os target mondjuk linux-ra. Onnan a community kihozhatja a többit ha nagyon kell. A wasm lehetne optimalizálni is képes compiler, ami viszont gyorsan is fordít (jai-szerűen gyorsan?), de ha nagyon-nagyon release-t akarunk, akkor ott lenne a C/C++ kimenet.
|
||||||
|
|
||||||
|
A C/C++ kimenettel az a nehéz, hogy egyeztetni kell a szemantikáinkat - de mivel nem valami extra bonyolult programnyelvet csinálunk, ez talán ugye nem is olyan nehéz ;-)
|
||||||
|
|
||||||
|
- Természetesen érdemes elgondolkozni, hogy eleve adjunk-e build-system-et, vagy ne. Ez egy érdekes kérdés. Ha nem adunk, akkor a modul lookup stb. is bonyolódhat pl. de könnyebb beilleszteni a BASED programokat meglévő build rendszerbe amikor interop van (pl. makefile) és ugye kevesebb effort is megcsinálni. De inkább arra hajlok, hogy mi magunk, a fordítóval build-eljünk. Ez mondjuk még a "strukturált..." megvalósításától is függ, hogy miként is lenne jobb... Szóval valamennyire nyitott kérdés is...
|
||||||
|
|
||||||
|
## Csomagkezelés
|
||||||
|
|
||||||
|
Nem igazán támogatom, bár ha véletlen sikeres nyelv volna, nehéz lenne megoldani, hogy mások ne csináljanak hozzá. De a legjobb az volna, ha first-class támogatás lenne a manuális csomag / modul kezelésnek, a sima bemásolgatósnak...
|
||||||
|
|
||||||
|
## OS support
|
||||||
|
|
||||||
|
Linux-first, onnan aki szeretné implementálja át portolással más OS-re szerintem...
|
||||||
|
|
||||||
|
## LSP
|
||||||
|
|
||||||
|
- Sajnos egyetértek JonBlow-val és fasságnak tartom... De ha valaki akar, próbáljon írni egyet oké...
|
3
csv-helyett.md
Normal file
3
csv-helyett.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Érdekes alternatíva
|
||||||
|
|
||||||
|
https://www.youtube.com/watch?v=qWjhuXBrBfg
|
17
llvm-helyett.md
Normal file
17
llvm-helyett.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# LLVM helyett
|
||||||
|
|
||||||
|
Az LLVM baromi nagy és bloated (persze jó sok optimalizációt tud)...
|
||||||
|
|
||||||
|
Ezt a QBE-t találtam, ami ezred akkora, de közel 25-70% olyan gyors.
|
||||||
|
|
||||||
|
Még jobb: Az IL köztes reprezentáció igazából szöveges!!!
|
||||||
|
|
||||||
|
# Linkek
|
||||||
|
|
||||||
|
https://www.youtube.com/watch?v=JTjNoejn4iA
|
||||||
|
https://github.com/DoctorWkt/acwj/blob/master/63_QBE/Readme.md
|
||||||
|
https://c9x.me/compile/docs.html
|
||||||
|
https://c9x.me/compile/doc/il.html
|
||||||
|
https://archive.fosdem.org/2022/schedule/event/lg_qbe/attachments/slides/4878/export/events/attachments/lg_qbe/slides/4878/qbe.pdf
|
||||||
|
https://c9x.me/compile/bib/
|
||||||
|
https://github.com/tsoding/qbe-notes
|
Loading…
x
Reference in New Issue
Block a user