Literate programing

Kolokviální práce Pavla Starého

P109 Historie a trendy VT

 

 

Na začátku počítačových dějin byl každý počítač originálem s unikátní instrukční sadou, programovacími prostředky a operačním systémem (pokud nějakým). Programovalo se tedy pro konkrétní stroj a při tehdejším počtu počítačů nebyla velká potřeba přenášet programy jinam. Ani komunikace mezi programátory nebyla příliš důležitá, protože se řešily většinou jednorázové úkoly a výpočty. Psaní komentářů ve zdrojovém kódu by bylo v době děrných štítků těžko představitelné.

K jistému pokroku došlo s příchodem vyšších programovacích jazyků. Jejich normování slibovalo použitelnost programů i na jiných počítačích. Skutečnost však byla jiná, protože hardwarová závislost byla stále příliš silná a každý výrobce, který byl téměř výhradním dodavatelem překladačů k normě tu něco přidal, tu něco ubral, takže přenositelnost byla spíše diskutabilní. Navíc byl stále problémem fyzický přenos programů, protože např. magnetické pásky měly sice standardní šířku, ale souborové systémy byly nekompatibilní.

Postupem času se však, zejména díky zvětšující se masovosti produkce počítačů, dařilo řešit i tyto problémy a přenositelnost programů začala být předmětem pozornosti a spolu s ní potřeba dokumentace programu. Je sice pěkné, pokud můžu použít program, který napsal někdo jiný, ale v okamžiku, kdy do něj potřebuji zasáhnout, opravit chybu, nebo jej přizpůsobit pro své potřeby, musím být schopen se v něm vyznat. To ovšem předpokládá mnohem více než znalost syntaxe příslušného programovacího jazyka.

Trendem této doby, 70-tá a začátek 80-tých let, bylo strukturované programování. Objevila se spousta návodů jak psát čitelné programy a každá větší softwarová firma si vytvořila svoji vlastní 'štábní' kulturu. Přesto ani tyto postupy nedávaly uspokojivé výsledky. Ukázalo se, že řádky komentářů ve zdrojovém kódu mohou popsat účel proměnných nebo kratších částí programu, ale těžko mohou popsat základní myšlenky a principy většího programu nebo projektu.

Jedním z pokusů o řešení těchto problémů, který vyvolal velkou pozornost, byl článek Donalda Knutha v časopise The Computer Journal v roce 1984 nazvaný Literate programing.

Knuth navrhl přistupovat ke tvorbě programu jako k literatuře. Jak sám říká:"Změňme náš tradiční pohled na tvorbu programů. Místo toho abychom předepsali počítači co má dělat, zkusme vysvětlovat lidským bytostem co chceme, aby počítač dělal. 'Literární' programátor může být srovnáván s esejistou, jehož hlavním cílem je srozumitelné vysvětlení a vybroušený styl. Takový autor vybírá s tezaurem v ruce názvy proměnných a vysvětluje účel každé z nich. Snaží se napsat program, který je srozumitelný, protože jeho principy jsou popsány způsobem, který odpovídá lidskému myšlení a používá k tomu formální i neformální prostředky, které se navzájem doplňují." Knuth nazval svoji metodu Literate programing (LP) a doufal, že jako nikdo nepřizná, že píše 'nestrukturované' programy, stejně nepřizná, že jeho programy jsou 'neliterární' a jeho metoda se rozšíří. Nad tím, co to vlastně LP je a proč jen málokdo sdílí Knuthovo neskrývané nadšení, bych se chtěl zamyslet v této práci.

Programování s použitím LP spočívá v psaní dokumentace během tvorby programu. Programátor píše menší logické části kódu a k nim rovnou připisuje vysvětlení. Pro realizaci LP vytvořil Knuth systém WEB. Nazval ho tak protože to bylo jedno z mála třípísmenných slov, které nebylo spojováno s počítači. Zřejmě netušil, co se stane za pár let. WEB umožňuje integrovat do jednoho zdrojového souboru jak dokumentaci, psanou v nějakém dokumentačním jazyce, tak vlastní program psaný v programovacím jazyce. Knuthem implementovaný WEB podporoval TeX a Pascal,ale nejsou žádné překážky pro použití jiných jazyků. Příkladem může být Cweb Harolda Thimblebyho, který používá Troff/Nroff a jazyk C. Do souboru pro WEB se vkládají značky a konstrukce, které oddělují jednotlivé části a umožňují jejich pozdější spojení.WEB se skládá ze dvou procesů, které používají společný vstup. První je weaving, tedy tkaní. Při něm ze zdrojového souboru vzniká TeXový soubor, který po zpracování TeXem a vytištění představuje kýžené literární dílo. Text je rozdělen na kapitoly z nichž každá obsahuje výklad kódu, který je k ní připojen. Zde stojí za zmínku, že programátor při psaní programu nemusí postupovat metodou shora-dolů ani naopak. Program může být psán tak jak je obvyklé, tedy kombinací obou metod, přeskakováním od jedné části k druhé, což umožňuje čtenáři sledovat tok myšlenek autora a lépe odpovídá lidskému myšlení.

Druhým procesem je tangling, něco jako šmodrchání (jinde též cuchání). Při něm vzniká pascalovský program spojením částí kódu z jednotlivých kapitol. Podle Knutha lze tímto způsobem obejít omezení Pascalu, zejména nemožnost rozdělit program na více částí. Výraz šmodrchání je zcela na místě, protože narozdíl o předchozího výstupu je tento program pro člověka téměř nečitelný. Není v něm žádné odsazení, všechny identifikátory jsou převedeny na velká písmena a na jednom řádku je více příkazů. Je to proto, aby takovýto program přijalo co nejvíce kompilátorů. Jediné, co může napomoci orientaci, jsou čísla kapitol uzavřená do komentářových závorek.

Jak to vlastně vypadá ? Knuth ve svém článku předvádí možnosti WEBu na programu, který nalezne prvních n prvočísel. Já se zde pokusím prezentovat několik ukázek. Čtenář nechť promine, že výstup není v TeXu. Koneckonců na výstupním formátu z principu nezáleží.


1. Program pro tisk prvočísel. Následující program vychází z programu E. Dijkstry v "first example of step-wise program composition" na stranách 26-39 jeho knihy Notes on Structured programing, pouze je přeložen do jazyku WEBu. Začneme jako on rozložením programu na jeho části.

{Do složených závorek budu vkládat komentáře k WEBu. Toto je ukázka výstupu programu WEAVE. Program v WEBu je vždy rozdělen do malých kapitol, z nichž každá kapitola má na začátku volitelný popis a na konci volitelný kód. Každá kapitola má též své pořadovéčíslo.} <Program na výpočet prvního tisíce prvočísel 2>


2. Náš program nemá vstup, protože ho chceme co nejjednodušší. Proto definujeme konstantu m=1000. Program je schopen vypočíst prvních n prvočísel pro libovolné kladné přirozené m, pokud nepřekročíme možnosti počítače.

{Následuje první ukázka kódu. Ten je zapsán buď přímo, nebo se do 'špičatých' závorek uvede odkaz na jinou kapitolu. Program TANGLE pak spojením těchto částí vyrobí syntakticky korektní pascalovský program. Chtěl bych připomenout, že použitý Pascal je ten podle normy}

<Program na výpočet prvních tisíc prvočísel 2>=

program tisk_prvocisel(output);

const m=1000; <další konstanty programu 5>

var <proměnné programu 4>

begin< <tisk prvních m prvočísel 3>

end.

This code is used in section 1


3. Plán programu. Budeme se snažit o jednoduchost a až poté o efektivitu. Program možná nebude optimální, ale bude spolehlivý, dobře motivovaný (?) a rozumně rychlý. Rozhodněme se na tomto místě vytvořit pole p, do kterého uložíme všechna vygenerovaná prvočísla a oddělme generování od tisku.

<tisk prvních m prvočísel 3>=

<naplň pole p prvočísly 11>

<vytiskni pole p 8>

This code is used in section 2


4. Jak bude reprezentováno pole p ? Máme dvě možnosti. Můžeme vytvořit dostatečně velké pole booleovských hodnot a jejich pravdivostní hodnota na k-tém místě bude znamenat, zda je to či není prvočíslo. Druhá možnost, kterou zvolíme je pole integerů, kde k-tá položka pole bude k-tým prvočíslem.

{Jak uvidíme dále, může existovat více kapitol se stejným top-level popisem. Popis <proměnné programu 4> se pak složí ze všech takto označených částí kódu. Kde se tyto části nacházejí označuje řádek See also ...}

<proměnné programu 4>=

p:array[1..m] of integer; {prvních m prvočísel ve vzest. pořadí}

See also sections 7, 12, 15, 17, 23, 24

This code is used in section 2


 

5. Výstupní část. Zaměřme se nyní na druhou část programu. Není sice tak zajímavá, jako generování prvočísel, ale dříve, či později ji stejně budeme potřebovat. Rozhodněme se nyní, jakým způsobem budeme tisknout výstup. Bude to po stránkách s rr řádky ve cc sloupcích, které budou ww znaků široké. Pro tento program bude rr=50, cc=40 a ww=10, takže tisíc prvočísel bude na pěti stranách. program nepředpokládá, že m=rr*cc.

<další konstanty programu 5>=

rr=50; cc=4; ww=10;

See also sections 19

This code is used in section 2


6. Abychom program zbavili notace, která je čistě pascalovská {a představili si další možnosti WEBu}, použijeme definice maker pro výstupní operace.

{definice maker se může objevit mezi popisem a kódem kapitoly. Kapitola se tedy může skládat ze tří částí z nichž každá může chybět. Jednoduchá makra nahrazují část kódu identifikátorem, parametrická podobně a navíc nahrazuji #, kdekoliv se v definici makra objeví. Jsou povolena pouze makra s jedním parametrem}

define print_string(#) = write(#)

define print_integer(#) = write(#:1)

define print_entry(#) = write(#:ww)

define newline = write_ln

define newpage = page


7. Pro zpracování výstupu budou potřeba další proměnné. Když budeme tisknout novou stránku, page_number bude její číslo a page_offset bude index prvního prvočísla na této straně. Podobně p[row_offset] bude první prvočíslo na řádku.

{notace += znamená, že aktuální kapitola má stejné jméno jako některá předchozí a její kód bude k této připojen}

<proměnné programu 4>+=

page_number: integer;

page_offset: integer;

row_offset: integer;

c: 0..cc;


A jak vypadá zápis ve WEBu ? Zde je ukázka z druhé kapitoly.


@ Náš program nemá vstup, protože ho chceme co nejjednodušší. Proto definujeme konstantu |m=1000|. Program je schopen vypočíst prvních |m| prvočísel pro libovolné kladné přirozené |m|, pokud nepřekročíme možnosti počítače. \[Následuje první ukázka kódu. Ten je zapsán buď přímo, nebo se do 'špičatých' závorek uvede odkaz na jinou kapitolu. Program TANGLE pak spojením těchto částí vyrobí syntakticky korektní pascalovský program. Chtěl bych připomenout, že použitý Pascal je ten podle normy\]

@<Program na výpočet prvních tisíc prvočísel @>=

program tisk_prvocisel(output);

const @!m=1000; @<další konstanty programu @>@;

var @<proměnné programu @>

begin @<tisk prvních m prvočísel @>@;

end.


WEB používá @ jako escape character. @< a @> značí začátek a konec top-level názvu. @! způsobí podtržení. WEB sám generuje čísla kapitol a řádky This code is used .. a See also. Nakonec uvádí i místa použití všech proměnných. Zde se použije podtržení, které značí, kde je proměnná deklarována. Proč se LP výrazněji neprosadilo? Knuth byl tímto svým vynálezem velmi nadšen. Udává, že jeho programy psané pomocí WEBu jsou nejen čitelnější, ale navíc mnohem efektivnější a obsahují mnohem méně chyb. Jeho argumenty, které se opírají o jinou filosofii psaní programů jsou velmi přesvědčivé. Knuth navíc do WEBu přepsal TeX, aby demonstroval jeho použitelnost pro rozsáhlé systémy.Možnou odpověď napsal sám Knuth na konci svého článku. WEB totiž klade na autora programu zvýšené nároky. Musí rozlišit a zvládnout čtyři druhy chyb. Syntaktické chyby v Texu, Pascalu a WEBu samotném a samozřejmě chyby algoritmické. WEB je příliš zaměřen na skupinu počítačových odborníků, kterých je mezi autory stále menší procento a při vývoji komerčních aplikací téměř absentují. Tito, zejména akademičtí pracovníci asi z větší části necítí potřebu programy si vyměňovat a jejich četbou si zpestřovat večery i když věřím, že případného zájemce by právě program napsaný ve WEBu plně uspokojil. Použití WEBu mimo tento okruh je patrně shledáváno zbytečným.

Pokud vím, pro platformu Wintel neexistuje jednoduše použitelná, nejlépe integrovaná podpora principů LP. Ani podpůrné prostředky typu CASE nelze považovat za náhradu možností WEBu, zejména co se přehlednosti a srozumitelnosti samotného programu týče.