Hlavní navigace

Co mě se_e na programování

10. 7. 2014 16:13 Tomáš Bleša

Uvědomuji si, že jsem se zabývám programováním už 28 let. Ano, bylo to ZX Spectrum, pokud vás to zajímá. Ve 14ti letech jsem si začal hrát s Basicem, pak jsem rychle přešel k assembleru, Pascalu, následovalo Céčko, …, až jsem nakonec na dlouhou dobu zakotvil u Javy, která mě živí.

Poslední dobou se nemůžu zbavit pocitu, že v některých oblastech stále používáme způsoby a koncepty programování, které nejsou hodny možností dnešních technologií. Dovolím si uvést pár příkladů, které mě nejvíc dráždí.

Persistence

Při tvorbě softwaru si občas připadám hloupě, protože většinu času programuju některou z těchto oblastí: ukládání dat, načítání dat, přenášení dat nebo konverze dat. Jsem v tu chvíli degradován na jakéhosi vidláka, který nepřehazuje hnůj, ale nuly a jedničky. Sedím pak u notebooku a v podstatě se nudím, protože se musím soustředit na to, abych neudělal syntaktickou chybu v SQL selectu a abych dobře poskládal data do řady.

Proč není možné mít podporu persistence přímo v programovacím jazyku? Na to se ptám už minimálně 5 let. Pokud si tady začnu vymýšlet virtuální Javu mých snů, tak bych chtěl mít možnost napsat tohle:

persistent Company    mojeFirma;

if (mojeFirma == null) mojeFirma = new Company(“Firma s.r.o.”);

Dostal bych proměnnou mojeFirma a v ní bych měl uložený objekt Company tak dlouho, jak bych jen potřeboval. Nechci také mazat data z žádné databáze. V okamžiku, kdy bych udělal tohle:

mojeFirma = null;

tak by objekt s textem “Firma s.r.o.” zmizel, protože by na něj nebyl žádný odkaz. Fungoval by prostě jakýsi garbage collector, ale ne jen pro RAM paměť, ale prostě pro paměť jako takovou. Čímž se vlastně dostávám k problému, který se týká neustálého rozlišování volatilní rychlé paměti a permanentní pomalé paměti. Já fakt nechci neustále přehazovat data mezi dvěma typy pamětí.

Dle mého názoru musí SQL databáze (v podobě, v jaké je známe dnes) vychcípat. Když se nad tím zamyslíte, tak v oblasti ukládání dat děláme pořád to, co jsme v RAM paměti přestali dělat před 15ti lety. Když chceme uložit objekt Address do databáze, tak si napřed musíme nachystat skrukturu ( create table), pak to tam vložit ( insert) a až to nepotřebujeme, tak musíme myslet na odstranění záznamu ( delete). Já už hodně dlouho nemusím řešit uvolňování RAM paměti, tak proč to musím řešit jinde?

Práce s SQL databází mi připomíná low-level práci s přidělováním paměti. Zjistíte si, jak je velká struktura, pak si přidělíte pole bytů, nakopírujete tam data a po skončení pole uvolníte.

Dál tuhle kapitolku asi nemá cenu rozvádět. Jako programátor chci pouze přemýšlet o tom, který objekt si chci nechat na delší dobu a který potřebuju jen dočasně. Nechci přemýšlet o volatilní a ne-volatilní paměti.

Networking

Komunikace po síti je networ, se kterým nechci neustále bojovat. Je to vlastně podobný problém. Proč bych měl řešit, jestli se nějaký kus dat nachází na lokálním heapu a nebo někde jinde na jiném stroji. Co, kdyby šlo třeba napsat:

mojeFirma = new Company();

a jako programátoři byste nemuseli řešit, kde jsou data fyzicky uložena. Všichni dobře víme, že proměnná je ve skutečnosti ukazatel do nějaké paměti. Proč nemůže existovat univerzální ukazatel (pointer), který by byl schopen adresovat data v RAMce, na disku nebo někde v oblacích.

Ano, uvědomuji si, že odstínění programátora od konceptu disku nebo sítě přináší nové výzvy, ale domnívám se, že i ty jsou uspokojivě řešitelné. Jako programátoři jsme zvyklí, že řádek:

speed = maxSpeed / 2;

nemůže vyhodit žádnou výjimku. Pokud ale bude možné, že maxSpeed je kus dat v cloudu nebo na nějakém jiném zařízení, tak může klidně nastat IO error. Stejně tak je trochu problém, že nemůžeme udělat žádný předpoklad o tom, jak rychle tento příkaz proběhne a tím pádem se dostávám k dalšímu bodu.

Neustálé tahání dat místo tlačení

Jako programátoři jsme zvyklí neustále pracovat sekvenčně a synchronně. Jelikož jsem už ve 14ti letech psal na ZX Spectru v assembleru, tak si dokážu představit, co procesor dělá, když se napíše:

speed = maxSpeed/2;

CPU vezme integer z RAMky, kam ukazuje pointer maxSpeed a uloží jej do registru, tam udělá shift, čím jej podělí dvěma a obsah registru přesune do RAMky na místo, kde ukazuje speed. Celá operace se provede za 35 nanomžiků. Pokud po tomto řádku dojde ke změně proměnné maxSpeed, na speed už to nemá žádný vliv.

Mozek takhle nefunguje, protože příroda možná sama vyhodnotila, že to je stupidní způsob. Pokud bychom měli v mozku dva neurony s názvem maxSpeed a speed, určitě by se neuron speed neptal neustále toho předchozího, jestli se nezměnil stav. Všechna data jsou tlačena dopředu, nikoliv tahána zezadu. Jakmile je neuron maxSpeed excitován, okamžitě informuje následující neuron, který provede dělení dvěma a hned pošle neuronu speed nový výsledek. Mozek prostě pracuje jen když musí.

Když šlápneme bosou nohou na kus skla, tak noha prostřednictvím nervů vyšle do mozku “event” a mozek ihned reaguje. Kdyby byl Bůh programátor, tak by mozek možná fungoval takhle:

do {

  Connection con = Noha.connect();

  PainStatus ps = con.readPainStatus();

  if (ps.isPain()) { sayAu(); }

  Thread.sleep(100);

} while (true);

To by Bůh pěkně podělal, protože se tam nezavírá con. Když se vrátím zpátky k počítačům a nechám mozek stranou, tak se teď pokusím vysvětlit, co je dle mého názoru správný způsob. Řádek

speed = maxSpeed/2;

by neměl být vnímán jako jednorázová operace, ale spíše jako vytvoření malinké asynchronní sítě, která spojuje uzel maxSpeed s uzlem dělení a dále na uzel speed. Uzel maxSpeed může být klidně persistentní hodnota na jiném stroji v cloudu a když jiný klient zapíše do maxSpeed nové číslo, chci aby se mi automaticky změnilo to, co je v uzlu speed. Když udělám webovou stránku, ve které budu mít napsáno tohle:

<div>Vaše rychlost je <b>{{speed}}</b> km/h</div>

tak se mi s přiměřenou prodlevou projeví změna v browseru. Jenom se musím postarat, aby browser věděl, co je  speed.

Programy, které teď běžně píšeme, vlastně dělají opakovaně tuto posloupnost:

  1. Chci vědět hodnotu X z úložiště Y.
  2. Zahájím vytvoření spojení na úložiště Y a čekám než se vytvoří.
  3. Zahájím hledání X a čekám než se najde.
  4. Přečtu X a vytvořím si jeho kopii X’.
  5. Ukončím spojení na úložiště Y a začnu pracovat s X’, což může být ale úplně něco jiného, než jsem původně chtěl (X už může mít jinou hodnotu). Pro lepší UX si X’ uložím do cache a za minutu se znovu zeptám úložiště Y na čerstvou hodnotu.

Takhle se dneska běžně programuje a inteligentnost celého řešení bych přiblížil následující metaforou:

  1. Chci se napít vody.
  2. Zahájím výstavbu vodovodu k nejbližší přehradě a čekám na dokončení.
  3. Zahájím výstavbu čističky vody a čekám na dokončení.
  4. Napustím si vodu z vytvořeného vodovodu.
  5. Zbořím čističku a vodovod. V případě, že budu mít ještě žízeň, pokračuji bodem 2.

Naštěstí v oblasti, o které mluvím v tomto budě se už začíná něco dít. Doufám, že funkcionální programování a tzv. reaktivní programování nás posune správným směrem.

Snad se dočkám toho, že přestaneme přešmodrchávat data mezi diskem, pamětí a sítí. Doufám, že za deset let už nebudu muset trávit čas tím, že budu řešit, jak z SQL dat udělat objekty, ty pak předělat na JSON, ten pak přesunout přes HTTP(s) a pak z toho zase vyrobit jiné objekty a z těch zase udělat DOM, atd.

Smrt SQL. Smrt HTTP. :-)

Sdílet