Nic

Programování je jen aplikovaná matematika a matematika je jen filosofie exaktními prostředky. Tahle souvislost je většinou pohřbena hromadou technikálií a často i zuřivě odmítána. Když ale vybublá na povrch, je zábava. Náhodný kolemjdoucí pak občas nedokáže poznat, jestli se se projednává kus kódu nebo scholastika Tomáše Akvinského. Tak jsme se jednou dostali s kolegou do ostré pře o tom, co je nic.

Začalo to nevinně tímhle kouskem Java kódu:

Optional.ofNullable(someObject)
	.map(SomeClass::getSomeAttribute)
	.map(OtherClass::getOtherAttribute)
	...
	.orElse("");

Tohle dělám docela často, protože jsem si myslel, že to je programátorsky korektní verze tolik užitečného elvis operátoru. Jedno ráno mě ale z ničeho nic popadla mrazivá paranoia. Účel map() je, funkcionálně vzato, pouze vzít návratovou hodnotu funkce a předat ji typovému konstruktoru příslušného funktoru (zde třída Optional). Z tohoto pohledu od map() přeci nelze očekávat, že konkrétně návratovou hodnotu null bude místo zabalení do funktoru transformovat na funktor samotný, protože od toho je tu úplně jiná funkce - flatMap(). Zápis elvis operátoru by s ní ale byl o mnoho složitější:

Optional.ofNullable(someObject)
	.flatMap(obj -> Optional.ofNullable(obj.getSomeAttribute())
	.flatMap( ...

Pokud by ale obyčejný map jen tupě předal null a tvářil se, že to je standardní hodnota, pak by se další map v řadě pokusil vyvolat instanční metodu na null objektu. S hrůzou jsem si představil to minové pole NPE, kterou jsem touhle chybou zanechal v našem kódu a okamžitě jsem otevřel zdrojá‎ky Optional. Tam mě naštěstí uklidnila tahle implementace map():

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
	Objects.requireNonNull(mapper);
	if (!isPresent())
		return empty();
	else {
		return Optional.ofNullable(mapper.apply(value));
	}
}

Abych byl klidnější, napsal jsem ještě pár testů a šel rozdýchat infarkt. Tady by to mohlo skončit happy endem, ale realita je samozřejmě jako vždy trochu složitější a temnější. V rámci ranního kuchyňského smalltalku jsem se o radostném poznání podělil s naším programátorským stařešinou (kdo z vás pamatuje COBOL, he?) a tím začíná druhý akt.

Dotyčný kolega mě setřel konstatováním, že takové chování je přeci z (matematické) podstaty věci nevyhnutelné, protože null je reprezentace ničeho a ve světě, kde je nic reprezentováno pomocí Optional.empty() (dovolte mi ve zbytku textu užívat Scalovské pojmenování příslušného typového konstruktoru “None”) se jakýkoli výskyt null musí okamžitě transformovat na None.

Začala lítá argumentační řež, protože já jsem byl naopak intuitivně přesvědčený, že matematickým zákonům odporuje právě takto svévolná transformace a i pokud ne, pak je alespoň její provedení zcela arbitrární volbou. Snažil jsem se dokázat to implementací map() pro Maybe v Haskellu, jakožto ultimátního funkcionálního jazyka, ale u jazyka, který prakticky nezná null, je to bohužel irelevantní:

instance  Functor Maybe  where
    fmap _ Nothing       = Nothing
    fmap f (Just a)      = Just (f a)

Naštěstí měl kolega, který byl toho času scallista, zrádce ve svém vlastním týlu. V zápalu boje otevřel zdrojáky Option ve Scalla a ejhle:

@inline final def map[B](f: A => B): Option[B] =
	if (isEmpty) None else Some(f(this.get))

S vítězným úsměvem jsem odešel z doutnajících trosek Scala týmu a nechal je smutně odhadovat kolik skrytých NPE tam asi mají. Tady už to minimálně pro Scallu happy endem nekončí, ale může pro ten nešťastný null vůbec nějaký happy end existovat? Intuitivně jsem věděl, že i to Javovské zjednodušení musí mít nějakou fatální vadu, bylo jen potřeba tu intuici nějak zformulovat.

A tady přichází na řadu ta filosofie. Ačkoli null i None reprezentují nic, jsou to velmi rozdílná nic. None je singulární bezrozměrné nic, zatímco null je známka absence očekávané hodnoty, je to nic implicitně definované absencí něčeho. U informací ale platí paradoxní pravidlo, že absence informace JE informace. Jinými slovy null je v typovém systému standardní hodnota nesoucí informaci a o tuto informaci při převodu na None přijdeme. To se musí někde projevit, jen jsem nevěděl kde.

Potupně se přiznám, že jsem na to přišel až s Googlem, ačkoli je to zcela jednoduché. Svévolné nahrazení null za None porušuje zákony kompozice a identity funktoru. Javovský Optional obětoval funkcionální záruky ve jménu jednoduššího zápisu, happy end se nekoná. A teď mě omluvte, mám pocit že jsem ve svém zeleném čaji zahlédl pár písmenek Matrixu…