Blog | Budoucnost programovacích jazyků

Budoucnost programovacích jazyků

Při vývoji nové funkcionality můžeme při implementaci mluvit o 2 faktorech, které vývoj prodlužují a o 2 faktorech, který vývoj krátí.

Prodlužující faktory:

  • Esenciální složitost – samotná složitost problému, tzn. složitější problém vyžaduje delší popis a ikdyž vypustíme všechnu syntax, zůstanou nám jen názvy proměnných, operací, cyklů, rekurzí, které vedou k řešení problému
  • Druhotná složitost – vzniká složitostí v jazyce, kdy nás jazyk nutí přidávat konstrukty, které nevedou přímo k řešení problému, ale mají jiný smysl (např. uvádění typů, různé () {} [] ; atd.), zároveň do této skupiny patří testy, vývojový kód, ošetřování chyb, uzpůsobování na daný hardware, práce s cache atd.

Když dáme dohromady esenciální složitost (tak je nezávislá na jazyce, je spíš vázaná na teoretickou informaci a kolik „informací“ bude nutné k popisu problému) + druhotnou složitost (kterou volí jazyk, infrastruktura, implementace), dostaneme složitost, kterou potřebujeme k vyvinutí takového řešení.

Pak tu máme zase zrychlující faktory, které nás dostanou dřív k cíli.

Zrychlující faktory

  • existující knihovny a frameworky – pokud mám vyvíjet algoritmus a součástí toho je i sort, tak buď můžu napsat svůj sort, nebo použiju existující. Pak můžu složitost snížit přesně o tu implementaci sortu. Čím blíž jsou zvolené knihovny, frameworky, CMS k tomu, co potřebuji, tím méně zbývá věcí k implementaci. Zároveň je nutné si uvědomit, že při přidávání knihoven a frameworků roste druhotná složitost daná tím, že musíme existující knihovny ohnout tak, aby uměly to, co potřebujeme. Pokud druhotná složitost použití knihovny překoná zrychlení způsobené tím, že použitá knihovna už obsahuje algoritmy a datové struktury, použít danou knihovnu nebo framework se nevyplatí. Existuje zde i lidský faktor, ten ale opomíjím, jde mi o „absolutní“ hodnocení bez přihlédnutí k tomu, jestli věc vyvíjí člověk nebo nějaká umělá inteligence.
  • DSL – tak, jako může ušetřit čas algoritmus, může ušetřit čas syntaxe, protože třeba v Haskellu často pracujete s monádami, jsou zde operátory >>=, → a do, které usnadňují práce s monádami, přestože nejsou nutné a dá se bez nich obejít (monády můžete používat i v C, stejně jako je můžete psát v Haskellu bez zmíněných operátorů, ale bude to zdlouhavější). Pokud daný jazyk má větší množství takových rozšíření + má-li i knihovnu pro daný problém, můžeme mluvit o DSL (Domain Specific Language), jazyce, který se hodí pro řešení daného problému.

Pokud budu chtít (dnes již) triviální věc, jako je napsat funkci, která k dnešnímu datu přidá zadaný počet dní, můžeme popsat problém esenciálně:

přidejPočetDníKDnešku(dní) = dnes() + dní*den

Přidáme si druhotnou složitost u jazyka, který nemá moc dobrou knihovnu pro práci s daty + který má určitou syntax.

import System.datum;

(Datum) přidejPočetDníKDnešku (Číslo dní) {
        Číslo početVteřinVeDni = 86400;
        Číslo dnes = System.datum.dnes().doVteřinOdPočátku();
        return new Datum(dnes + dní*početVteřinVeDni);
}

Teď si přidáme dobrou knihovnu.

import System.datum.lepší;

(Datum) přidejPočetDníKDnešku (Číslo dní) {
        return System.datum.dnes.přidej(dní, System.datum.DEN);
}

Mít DSL by vyžadovalo mít jazyk, kde je možné právě rozšiřovat syntaxi. Já si tu přidám jazyk, který umí flip (prohodí 1. a 2. parametr funkce), má automatic partial application a funkce jsou přímo jazykový konstrukt + je možné rozšiřovat jazyk a this přijde jako první parametr funkce. Pozn: vše je pořád staticky typované, jazyk si typy odvodí inferencí. Vytvářím tu tedy jazyk s nižší druhotnou složitostí a ještě navíc zjednodušuji problém pomocí DSL.

import System.datum.lepší;
Number.extends.dníOdeDneška = flip(System.datum.dnes.přidej(System.datum.DEN));

Použití je pak: 5.dníOdeDneška()

Toto řešení může být problematické, pokud tím rozšíříme syntaxi pro celý program, správně má být takového rozšíření v samostatném modulu a tento modul si importujete jen tam, kde budete potřebovat.

Až doteď jsme se obešli bez jednoho fenoménu. Totiž takového rozšiřování syntaxe má své limity. V situaci, kdy si chcete přidat třeba svou řídící strukturu, jste s věcmi, které jsem zatím zmínil, nahraní. Může vám pomoci to, že použijete lazy jazyk, to vás posune o kousek dál.

Takhle si přidáte if.

A if (Boolean expr, A a, A b) {
        when (expr()) {
                return a;
        }
        when (! expr()) {
                return b;
        }
}

V jazyce, který má eager evaluation (prakticky všechny mainstreamové jazyky), se vykoná a i b ještě předtím, než se vykoná tělo funkce if, takže se vykoná jak blok pro true, tak blok pro false, což je pomalé (a pokud v nich máte nějaké side-effecty, tak můžete mít problémy o řád větší).

Jenže i lazy evaluation má své limity. Navíc některé věci (např. I/O) se s nimi dělají obtížně. Pro programátora je jednoznačně pohodlnější psát v jazyce, kde se věci vykonávají tak, jak vývojář určí.

A teď už se dostáváme k samotnému vrcholu programovacích jazyků. Jedná se o takzvané homoikonické jazyky s makry. Jedná se o jazyky, v kterých je možné přidat prakticky cokoliv. Stojí na vrcholku a uživatel takového jazyka je v jiné pozici, než uživatel jiného programovacího jazyka. Možnosti přidat si libovolnou další syntax dělá z uživatele jazyka zároveň i návrháře jazyka (rozhodne-li se tak).

Homoikonické jazyky jsou takové, v kterých syntax kódu jazyka je zároveň syntaxí datové struktury. Z těch známých se jedná o jazyky z rodiny Lispů (Common Lisp, Scheme, Clojure), ale dál mezi ně patří i Elixir nebo Prolog.

Když do takového jazyka přidáte makra, můžete je zapisovat přímo v daném jazyce, protože se pořád jedná jen o vytváření (nebo modifikaci) existujících datových struktur.

Líbí se vám operátory z Haskell monád? Přidejte si je.
Líbí se vám go blocky z Go? Přidejte si je.
Chcete něco jako "kanonický přenositelný commonlispový tracer, který atomicky nahradí funkci za její trasující wrapper a pak po skončení trasování zase zpátky přiřadí původní funkci"? Přidejte si ho.

Homoikonické jazyky s makry jsou konečným vývojovým stádiem programovacích jazyků. Nemůže být nic lepšího, protože když nějaký jazyk přijde s něčím novým, může si vývojář v homoikonickém jazyce přidat takovou věc taky. Pokud je daný jazyk Turingovsky úplný, nemáte šanci ho překonat.

Na tomto backgroundu předvídám budoucnost programovacích jazyků.

V budoucnu budou jazyky směřovat k následujícímu:

  • méně druhotné složitosti (dynamické jazyky + statické jazyky s typovou inferencí; testy a kontrakty přímo v jazyce)
  • makra (lépe nad homoikonickým jazykem, hůře nad AST)
  • existující knihovny, syntaxe, frameworky a šablony software, které řeší časté problémy

Programování

Předejte zkušenosti i dalším a sdílejte tento článek!



Jiří Knesl
Business & IT konzultant

Jiří Knesl poprvé začal programovat v roce 1993. Od té doby, díky skvělým učitelům a později zákazníkům, měl možnost neustále růst v oboru vývoje webových aplikací a informačních systémů. v roce 2002 se přidal zájem o ekonomii a v roce 2006 o organizaci práce. Vším tím se konstantně profesně zabývá jak ve svém podnikání, tak i u zákazníků. Za posledních 5 let vydal na tato témata přes 400 článků.

Prohlédněte si moje reference

Mám zkušenosti z rozsáhlých projektů pro korporace, velké podniky, střední i malé firmy, ale i pro startupy v cloudu. Zvyšoval jsem jejich know-how, pomáhal nastavovat jejich organizační strukturu, byl lektorem a mentorem v náročných situacích. Podívejte se, jak vidí můj přínos samotní klienti.

Sledujte mé postřehy na sociálních sítích