Subroutine vs. coroutine
Programování nemusí vždy být inspirujícím a povznášejícím cvičením intelektu - zvláště chápeme-li jej v užším slova smyslu jako tzv. kódování (coding), neboli jako přepisování již vymyšleného a již rozpracovaného do příslušného programovacího jazyka. Přesto se ale i při kódování musí pečlivě zvažovat, co a jak naprogramovat, zvláště jde-li o alespoň trochu rozsáhlejší program, který má něco smysluplného dělat.
Jedním z nejdůležitějších momentů při kódování je správně rozpoznat, které činnosti se opakují, byť s případnými drobnými obměnami. Většina programovacích jazyků totiž umožňuje naprogramovat takovéto činnosti právě jednou, a pak je opakovaně využívat (volat). Podívejme se podrobněji, jaké mechanismy a konstrukty za tímto účelem programovací jazyky nabízí.
Na úrovni jazyka symbolických adres, neboli asembleru, jsou takovýmto mechanismem podprogramy (subroutines), zatímco na úrovni vyšších programovacích jazyků jde o nejrůznější procedury, funkce či jiné druhy procedurálních bloků. Obě tyto kategorie přitom dosti úzce souvisí - překladem procedury, funkce či jiného procedurálního bloku vyššího programovacího jazyka obvykle vzniká podprogramu. Co je ale charakteristické pro podprogram? Zkusme se na něj podívat z poněkud netradičního úhlu, který nám umožní srovnat jej s jiným mechanismem, kterým jsou tzv. koprogramy.
Podprogram jako takový není nic jiného než „kus kódu", který má čistě statickou povahu - existuje po celou dobu existence programu. Jakmile ale nějaký podprogram spustíme (zavoláme, předáme mu řízení), vzniká nový objekt, který má již ryze dynamický charakter, a který bychom měli označovat jako dynamický exemplář či instanci podprogramu. O co jde, je nejlépe patrné na představě podprogramu, který realizuje proceduru či funkci vyššího programovacího jazyka s lokálními proměnnými: jakmile je podprogram spuštěn, vytvoří si (nejspíše na zásobníku) jednu sadu svých lokálních proměnných. Jakmile daná procedura-podprogram skončí, příslušná instance lokálních proměnných zanikne. Nebo ještě lépe: pokud nějaký podprogram (procedura, funkce) volá rekurzivně sebe sama, kód tohoto podprogramu existuje v paměti stále jen jednou, ale dynamických instancí podprogramu (včetně instancí lokálních proměnných a dalších lokálních objektů) existuje tolik, kolik proběhlo rekurzivních volání.
Nyní si uvědomme další důležitou skutečnost: podprogram (subroutine) není určen k tomu, aby se z něj dalo „vyskočit ven", a někdy později se zase „vrátit zpět". S jedinou výjimkou, kterou je volání jiného podprogramu, nebo rekurzivní volání sebe sama. Takovéto vnořené volání podprogramu však vždy musí skončit přesně opačným pořadím návratů při ukončení příslušného podprogramu: jestliže například podprogram A volá podprogram B, který zase sám volá podprogram C, pak podprogram C nemůže vrátit řízení zpět programu A. Jakmile skončí, vrátí se řízení zpět podprogramu B, a teprve při skončení podprogramu B při se řízení vrací zpět podprogramu A. Pokud bychom chtěli vzájemná volání podprogramů znázornit graficky, dostali bychom v každém okamžiku vždy jen jednu lineární posloupnost do sebe vnořených instancí, představují jedinou tzv. linii výpočtu. Tato posloupnost by se přitom vždy chovala jako zásobník - řízení by měla vždy ta instance, která by se právě nacházela na vrcholu, a při svém skončení by předávala řízení instanci bezprostředně pod sebou, která by současně stala novým vrcholem. Nebo jinak: dynamické instance se při vzájemném volání podprogramů vždy vrší na sebe, a v obráceném pořadí se zase z vrcholku odebírají (a zanikají).
Pokud bychom chtěli dosáhnout toho, aby si podprogramy mohly předávat řízení bez jakéhokoli omezení, nesměly by to již být podprogramy ve výše uvedeném smyslu. Muselo by jít o tzv. koprogramy (coroutines).
Každý koprogram představuje samostatnou linii výpočtu - neboli samostatný zásobník, na který se postupně vrší jednotlivé dynamické instance. Předání řízení z jednoho koprogramu do jiného koprogramu potom vlastně představuje „přeskok" z jedné samostatné linie výpočtu na jinou, resp. přeskok z jednoho pomyslného zásobníku dynamických instancí na jiný. Díky tomu již není nutné zachovávat žádné definované pořadí při předávání řízení, takže například koprogram A může předat řízení koprogramu B, ten koprogramu C, a koprogram C může předat řízení přímo koprogramu A.
Koprogramy jsou základním mechanismem, který umožňuje realizovat nejrůznější druhy pseudoparalelismů - neboli provozovat více výpočtů nikoli skutečně paralelně, ale takovým způsobem, že se velmi rychle střídají ve svém provádění, a přitom si vzájemně a z vlastní iniciativy předávají řízení.
Většině programovacích jazyků vyšší úrovně je pojem koprogramu cizí - to platí zejména pro ty programovací jazyky, které se již svou koncepcí snaží přimět programátory k dodržování zásad „slušného chování", neboli zásad strukturovaného programování. Není divu, neboť možnost chaotického „přeskakování" výpočtu z jedné větve na jinou se se zásadami strukturovaného programování skutečně příliš neslučuje. Na druhé straně existují i takové programovací jazyky vyšší úrovně, které s existencí koprogramů v nějaké podobě počítají. Jedním z prvních takovýchto jazyků byl jazyk Simula 67, prapředek všech dnešních objektově orientovaných jazyků. Koprogramy však zdaleka nejsou jen záležitostí minulosti - mají například dost společného s dnes tolik populárními vlákny (angl. threads). Ale o tom zase až někdy příště.