# Poučení z DÚ04: jak psát pěkný kód
V průběhu opravování čtvrté úlohy se některé typy chyb opakovaly velmi často. Zde je jejich seznam, abyste se jich mohli v budoucnu vyvarovat.
* **Není všechno if, co vrací Bool**: `if p then True else False` je ošklivé a nemá smysl nepoužít samotné `p`, jakkoli je ten výraz složitý. Často se taky objevovalo (spolu s dalším bodem) `if p then True else whatever`. Od toho máme operátor disjunkce, který díky lenosti funguje stejně: `p || whatever`.
* **Test prázndosti seznamu**: Správná varianta je `null xs`, která funguje nezávisle na typu seznamu a funguje i na nekonečných seznamech (oproti `xs == []`, který vyžaduje porovnatelné prvky a tudíž není univerzálně použitelný, nebo `length xs == 0`, který prochází celý seznam).
* **První prvek seznamu** zjišťujeme pomocí vzorů (pokud to jde) nebo pomocí funkce `head`; oproti výrazu `xs !! 0` je to čitelnější. „Poslání“ operátoru `!!` je převážně v přístupu k *n*-tému prvku, kde *n* neznáme dopředu (nebo je velké).
* **Závorky v if** nejsou v Haskellu potřeba: podmínka ani jednotlivé větve nepotřebují být v závorkách. Pokud závorky uvedete navíc, zbytečně to budí dojem, že je na něco potřebujete, což znepřehledňuje kód.
* **Závorky a infix**: není třeba závorkovat prefixová volání funkcí na jednotlivých stranách infixového operátoru: např. `head xs == last xs` je zcela v pořádku, prefixová aplikace váže těsněji než infixová (ekvivalentní výraz se zbytečnými závorkami je tedy `(head xs) == (last xs)`).
* Někdy jsou závorky sice navíc, ale mohou být užitečné, např. u výrazů jako `(x == y && y == z) || x /= w` -- zde sice lze závorky vynechat, protože `(&&)` má vyšší prioritu než `(||)`, ale výraz je lépe čitelný s nimi, protože se čtenář nemusí zamýšlet nad ne zcela zřejmou prioritou konjunkce a disjunkce.
* Pokud to jde, **používejte vzory** místo funkcí na rozebírání např. seznamů či dvojic; tj. při seznamové rekuzi používejte `(x:xs)` namísto vzetí celého seznamu a jeho rozebírání pomocí `head` a `tail` -- je to přehlednější.
* **Neřaďte seznamy zbytečně**: pokud potřebujete vybrat nejmenší/největší prvek, dělejte to pomocí `minimum`/`maximum`; je to rychlejší (tedy nepoužívejte `head (sort xs)` nebo `last . sort`).
* **Využívejte knihovnu a nevymýšlejte kolo**: funkce jako `length` a `sort` jsou samozřejmě k dispozici (a mnohé další zajímavé seznamové funkce v `Data.List`). Nepište si vlastní; vyhnete se chybám a ušetříte si práci.
* **Buďte konsistentní** v pojmenovávání; v Haskellu se obvykle používá `camelCase`, ale hlavně nemíchejte různé způsoby.
* **Zamýšlete se při pojmenovávání funkcí i proměnných** -- jméno by mělo být vypovídající; především u funkcí, které dělají něco konkrétního (u generických funkcí jako `map` je v pořádku používat názvy krátké názvy ve vzorech jako `(x:xs)`). V řešeních se často objevovaly funkce jako `trd3`, které vrací třetí prvek trojice. Čitelnosti ale velmi prospívá, pokud se táž funkce jmenuje `getShowRating`. Při čtení kódu, který ji používá, si čtenář nemusí dohledávat, co že je to uloženo v tom třetím prvku.
* **Neopakujte kód**: zvláště funkce `worstRating` a `bestRating` přímo volají po tom, aby měly společnou velkou část definice. V `publicationRange` se zase hodí neopakovat výraz, který zjišťuje roky episod (typicky nějaké `map getYear eps`) a dát jej do lokální definice; ušetří to nejen znaky při psaní, ale i čas při výpočtu.
* **Používejte typové aliasy jen tam, kde skutečně dávají smysl** -- to, že něco má typ `(Int, String, Int)`, neznamená nutně, že to je `ShowInfo`. Je matoucí pro to používat typ `ShowInfo`, pokud je význam jednotlivých částí trojice jiný než (ID, jméno, hodnocení).
* **Využívejte síly překladače**, např. přepínač `-Wall` vám může pomoci odhalit některé typy chyb, třeba nepokryté vzory
* **Zamyslete se jak daný problém vyřešit elegantně** -- `sum (map (\x -> if p x then 1 else 0) list)` není moc hezké, `length (filter p list)` je hezčí.
* Nezapomínejte na již dříve zveřejněné [tipy k psaní kódu](https://is.muni.cz/auth/discussion/predmetove/fi/podzim2019/IB015/codestyle/).