S ideou, že ve vyšším jazyku řeknu, co chci, a kompilátor to vymyslí co nejlépe, v zásadě souhlasím. A slyšel jsem nějaké anekdotické případy, kdy například GCC vyhrál nad ručně optimalizovaným assembly code, protože vývojáři kompilátoru byli zkušenější. Kompilátory (obzvlášť JIT) někdy dovedou zajímavé věci. Třeba OpenJDK a GraalVM dovedou z dynamické alokace udělat alokaci na stacku, pokud si odvodí, že proměnná má odpovídající životnost. Obávám se ale, že s podobným typem byste v Haskellu krutě pohořel. Rád bych viděl ten kompilátor, co něco takového dovede dobře zanalyzovat a optimalizovat. I pokud byste použil typ pro dvourozměrné pole (má Haskell něco takového?), potřeboval by kompilátor pár věcí, ke kterým jsem skeptický. Je tu pár úkolů, které by si vyžádaly hlubokou analýzu celého programu:
1. Zjistit, že je výhodnější striktní vyhodnocování. To není vůbec snadné. Co když uděláte nějakou náročnou operaci, ale nakonec z obrázku budete chtít jen malý výřez?
2. Vyřešit případné time/space tradeoffs. OK, možná striktní vyhodnocování sníží paměťovou náročnost, ale zvýší časovou náročnost. Co pak preferujeme?
3. Ujistit se, že striktní vyhodnocování je korektní, případně udělat plán B. Haskell má sémantiku líného vyhodnocování. Z hlediska chování to znamená, že mohu mít obrázek s vadným subpixelem (například v jeho výpočtu dělím nulou nebo nekonečný výpočet*), chyba se projeví, až ho budu číst a hlavně jenom pokud ho budu číst. To rozhodně není snadné analyzovat. Pokud si s tím kompilátor nemůže být jistý, potřebuje mít plán B (překopat datové struktury za běhu), což není sranda, a může to znamenat v podstatě náhlý zásek.
4. Následně se rozhodnout, že místo ukazatele na RGB bude výhodnější přímo samotná hodnota. To by teoreticky mohl kompilátor rozhodnout snadno, pokud ví o neměnnosti (v Haskellu OK), používá striktní vyhodnocování (vizte body výše) a může tam být jen jediný typ (žádná dědičnost, v Haskellu asi OK). Problém by trochu nastal v případě onoho plánu B, protože pak by musel překopat nikoli jeden pixel, ale rovnou celý obrázek.
Pokud byste nepoužil dvourozměrné pole, ale přímočaře [[RGB]], bylo by to pro kompilátor ještě složitější:
1. Ze samotného typu nevyplývá, že jde o obdélníkový obrázek. Ve skutečnosti může jít o obdélník s různě ustřiženým jedním (typicky pravým; záleží na interpretaci dat) okrajem, protože vnitřní seznamy mohou být různě dlouhé.
2. List je v Haskellu spojový seznam, se vším, co z toho plyne pro paměťovou náročnost a výkon. To není jen nějaký implementační detail. To Haskell vystavuje tím, že jednak můžete seznam rozdělit na head a tail, a jednak můžete k seznamu přidat nový head, a přitom zachovat původní seznam. Ano, teoreticky by se kompilátor mohl rozhodnout použít pole, čímž některé operace zlevní a jiné zdraží (jak z hlediska času, tak z hlediska paměti), ale opět není sranda toto analyzovat. To bych se ale opakoval.
BTW, nevím, k čemu by Haskellu byl parser a serializer. Typ RGB je v podstatě struktura tří 1B integerů, k tomu lze serializaci a deserializaci vygenerovat snadno automaticky.
*) Nabízí se tu argumentovat problémem zastavení. Jeho neřešitelnost nám nicméně neříká, že to nedovedeme vyřešit nikdy. Můžeme mít algoritmus, který bude říkat ano/ne/nevím, přičemž v tom „nevím“ budou jak případy, které se zastaví, tak případy, které se nezastaví. Každopádně pokud u aspoň jednoho výpočtu pixelu nevíme, kompilátor potřebuje onen plán B.