Učebicové OOP říká, že máme dát metodu save() přímo do User. Na první pohled to zní logicky a elegantně, ale je to v principu kravina. Ten User bude mít vazby na další třídy, třeba na Company a Address atp. Co bude v metodě save(), když byl vytvořen rovněž i Company a Address objekt a User na to má návazovnost, tzn. nemá smysl vytvořit Usera bez nové Company a Address? Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
(...)
Omlouvám se přítomným, že nejsem v oboru formálně vzdělán - a proto téma chápu do značné míry jako laik, naivní umělec, perpeťák - nepolíbený gramatikami, náročnou krásou funkcionálního programování apod. Kdo četl Neználka, a pamatuje si kuchaře, který vynalézal suchou vodu? Zároveň jsem dostatečně stár na to, abych chápal, že obvykle tu coderskou dřinu mi nakonec stejně nikdo neodpáře, ať se s vrstvením abstrakce trápím sebevíc a ať obracím sémantickou konstrukci naruby kterýmkoli z pěti způsobů.
Svého času někdy začátkem století mě podobné úvahy dohnaly jednak k tomu, abych si trochu prořezal zoubky o g++ v Linuxu a o vnitřnosti Perlu... a taky abych se zamyslel nad společnými rysy dnes (dodnes!) dostupných programovacích jazyků a DBMS.
Zrovna problém perzistence dat v běžných jazycích je hodně zajímavý. A přijde mi, že dědit nebo komponovat "invazivně" nějakou infrastrukturu pro perzistenci přímo do "mých" objektů je případem snahy, řešit něco na nesprávné vrstvě abstrakce. Správný postup podle mého: ve svém programu bych perzistenci neměl explicitně řešit! Perzistenci by měla zajišťovat nějaká "ODBMS mezivrstva", cudně skrytá za "metamodel" jazyka, ve kterém programuji. Řekněme, že by šmahem všechny objekty, které ve svém jazyce vytvořím, měly automagicky zajištěno perzistentní ukládání do nějakého "backendu pro zajištění perzistence". Takže jednou vytvořený objekt by nikam nezmizel, a třeba po vypnutí a zapnutí programu by ve "jmenném prostoru" jednoduše dál existoval.
Ta "perzistenční mezivrstva" spolupracující s metamodelem by mohla mít štelovatelné čudlíky: v zásadě by bylo na výběr write-back vs. write-through, a pokud write-back, tak třeba ještě dodržet aspoň pořadí transakcí, nebo se v zájmu průchodnosti vykašlat i na pořadí (logicky s rizikem nekonzistence dat v permanentním úložišti při pádu nebo ztrátě napájení).
Tzn. žádné user.save(). Volání konstruktoru 'new user()' by podle nastavení znamenalo buď: že v okamžiku návratu z konstruktoru je objekt již perzistentní, nebo třeba zatím není na disku ale brzy bude (a ve správném pořadí dle fronty s předchozími transakcemi), nebo na disku "zatím není a bude časem, až se to bude hodit" - každopádně při práci s objekty "user", firma, poštovní adresa apod. by se programátor nad nějakým save() vůbec neměl zamýšlet.
Referenční integrita mezi instancemi je další velice zajímavé téma. Dnešní jazyky mají "vztah mezi instancemi" zařízen objekty typu pointer, reference apod. Prostě jednosměrný ukazatel, u kterého v principu hrozí, že bude ukazovat kamsi do propasti. Jasně - pokud bude mít povinnou inicializaci zavěšením na existující cílový objekt, tak zrovna inicializace už je ošetřená. Ale co když ten cílový objekt časem zanikne, nebo bude potřebovat zaniknout? atd. Prostě mě tyhle úvahy dovedly k následující fintě:
Uvažme vztah mezi instancemi jako dvousměrný objekt. Speciální druh objektu v metamodelu programovacího jazyka. Měli bychom instance, a vztahy mezi nimi. Vztah mezi instancemi by mohl existovat jenom ve chvíli, kdy by existovaly obě "koncové" instance. Pokud by jedna z obou instancí zanikla, zanikl by vztah. Přesněji řečeno, dalo by se pravidly pro "vztahovou integritu" předem jasně určit (třeba v programátorem deklarovaných třídách), které vztahy jsou povinné, které nikoli, zda smí "protějšek ve vztahu" prostě svévolně umřít, nebo zda mu má být v úmrtí zabráněno, nebo zda se mají instance mazat v kaskádě apod. Užitečným speciálním druhem vztahu by mohlo být "vlastnictví"= vztah nadřízenosti a podřízenosti mezi instancemi. A samozřejmě stojí za úvahu, jak takové vztahy, které instance může mít ale nemusí, jak je implementovat v "jmenných tabulkách symbolů" v našem hypotetickém jazyce. V tradičních jazycích jako je kompilované Céčko, jakmile je deklarována proměnná, lze se na její jméno trvale odkazovat. Pokud se odkážu na jméno, které neexistuje, tak je to jasná compile-time chyba, protože existence/neexistence jména se nemůže měnit za běhu. V jazyce, kde vztahy průběžně vznikají a zanikají, by toto muselo být nějak ošetřeno. Výjimkou při pokusu o přístup ke vztahu s nenalezeným jménem, případně nějakým explicitním opatrným testem jako exists() apod.
Podle mého to vede na interpretovaný jazyk integrovaný s triviálním ODBMS enginem. Jaký back-end by se použil pro ukládání na disk, to je vcelku vedlejší. Já jsem tehdy před 15-18 lety sáhl po MySQL, ale zřejmě by to šlo naroubovat stejně dobře na libovolný key-value store, pokud bych nechtěl psát svůj vlastní backend nad holými soubory. Důležité je, že do storage back-endu by to ukládalo data v poměrně primitivním "datovém metamodelu". Datový model uživatelského programu by NEBYL součástí low-level datového modelu používaného na back-end databázi. Back-end DB by odpovídala "metamodelu", uživatelský datový model by žil "o vrstvu výš". Potažmo otěže referenční integrity na úrovni back-endové SQL DB nebo KV-storu by držel pevně v ruce primitivní ODBMS engine mého jazyka - a jak již řečeno, referenční integrita v metamodelu mého jazyka by byla řešena "obchvatem od lesa" :-) přes obousměrné vztahy mezi instancemi a jejich definovatelná "integritní omezení".
Plus je zajímavé se zamyslet, že by ta věc mohla být vícevláknová, jak tam udělat systémově nějaké jemnější zamykání mezi více uživatelskými vlákny apod. Uživatelské účty a uživatelská práva... tohle je zrovna dost nuda.
Pokud se týče back-endu, je třeba se zamyslet, jak řešit fungování jazyka v situaci, kdy nemůže držet všechny třídy a instance trvale v RAM. Že by se měl snažit, nechávat nějaká data na disku, a ládovat je až podle potřeby. Což znamená, implementovat v "ODBMS mezivrstvě" (ano tam to patří!) generický mechanismus, jak třeba při enumeraci vztahů nějaké instance natáhnout jenom "minimální kostru" sousedících vztažených instancí, a teprve on demand dotahovat celý jejich datový obsah. Nebo nedotahovat kostry sousedních instancí, ale jenom proto-vztahy? Toto jsem částečně rozpracoval (ošidil jsem to, zkrátil jsem si cestu v zájmu rychlejšího postupu.)
Další zajímavé téma je jmenný systém a na něj navázaný interní systém klíčů a odkazů v metamodelu a ODBMS.
Jak říkám, kdysi jsem si s tou myšlenkou
trochu hrál, ale na vlastní programovací jazyk nikdy nedošlo. Ten školácký prototyp, na kterém dnes vidím spoustu chybných rozhodnutí a dost divný coding style, vykazoval základní životní funkce objektově-hierarchické databáze. Šlo vkládat jednoduché třídy a podle nich potom instance. Povedlo se mi i zhruba embednout Perl, ale bylo mi jasné, že nevím jak dál. Jednak Perl při každé chybě shodil celý můj engine :-) a druhak jeho stávající metamodel byl s mým vybájeným metamodelem dost neslučitelný. Dalo dost sebekázně, napsat tam aspoň jednu-dvě primitivní "vložené procedury", které by nepadaly na perlovou syntaktickou chybu. Čili pokud jde o nějaký jazyk, který bych s tou svojí "databází" bezešvě integroval, musel bych začít s čistým listem papíru. A zhruba v tom okamžiku mi taky definitivně došel volný čas na blbosti :-) Rád bych se k tomu ještě někdy vrátil, ale uvidím, jestli to stihnu, než mi moje šedá kůra zdřevění natolik, že už to nedám.