Ty tři funkce k sobě nepatří, jen se spolu obvykle dodávají
Když se v Rustu otevře debata o pojmenovaných argumentech, přijde vždycky jako balík: pojmenované parametry, volitelné parametry, výchozí hodnoty. Jazyky, které je mají, je mají obvykle všechny, a proto se o nich mluví jako o jedné vlastnosti.
Nejsou to jedna vlastnost. Steve Klabnik dochází k tomu, že Rust může mít pojmenované parametry a přitom nemít volitelné ani výchozí. Ten balík drží pohromadě zvyk, ne návrhová nutnost.
Jedno přidává informaci na místě volání, druhé ji odebírá
Rozdíl mezi nimi se dá říct v jedné větě. Pojmenovaný parametr napíše na místo volání něco, co tam dřív nebylo. Volitelný parametr naopak způsobí, že na místě volání něco chybí a čtenář musí dojít do definice, aby se dozvěděl, co se tam vlastně stalo.
spawn(detached: true) řekne na jednom řádku víc než spawn(true). Ale spawn() s výchozím detached = false řekne méně než spawn(detached: false), protože ta hodnota je pořád v efektu a v kódu ji nevidíš.
Tohle je ten test, který se vyplatí pamatovat u jakéhokoli návrhu API: nese ta vlastnost informaci na místo použití, nebo ji z něj odvádí do definice. Podle téhle jediné osy se rozpadá i řada dalších sporů, třeba o implicitní konverze nebo o přetěžování.
Cena zůstala, výnos se změnil
Klabnik svůj postoj změnil, a zajímavý je důvod. Nezměnily se náklady té funkce v jazyce. Změnilo se, kdo ten kód čte.
Když volání funkcí čte a píše člověk, má k dispozici IDE, hover a povědomí o kódové bázi, ve které pracuje. Nepojmenovaný argument je pro něj drobná nepříjemnost. Když ho čte agent, který dostal do kontextu výřez souboru, je pozice jediná informace, kterou má, a jméno parametru z něj udělá jednoznačnost tam, kde by jinak musel dohledávat definici.
Výnos pojmenovaných parametrů tím stoupl. Cena volitelných a výchozích nestoupla ani neklesla — ty pořád informaci odebírají, a agentovi škodí ze stejného důvodu jako člověku, jen víc.
Tohle je obecnější než argumenty v Rustu. Návrhová rozhodnutí v jazycích i knihovnách jsou ceněná podle předpokladu o tom, kdo je čtenář a co má k dispozici. Když se ten předpoklad změní, nemusí se změnit žádný technický fakt a přesto se změní správná odpověď. Vyplatí se proto vědět, který ze svých návrhů stojí na jakém předpokladu o čtenáři.
Proč to stejně není zdarma
Klabnik není advokát, je průzkumník, a poctivě vypisuje, co by návrh musel vyřešit. Pořadí vyhodnocování argumentů, když je volající smí psát v jiném pořadí než definice. Kompatibilita s traity, kde implementace nemusí používat stejná jména jako deklarace. Otázka, jestli je jméno parametru součástí veřejného rozhraní, tedy jestli jeho přejmenování rozbíjí sémantickou verzi.
Ta poslední je ta nepříjemná. Pojmenovaný parametr přesune jméno z lokální proměnné do kontraktu. To je přesně ten mechanismus, kterým funkce získá čitelnost, a zároveň ten, kterým autor knihovny ztratí svobodu.
Co si z toho odnést
Když debata o jazykové nebo API vlastnosti nikam nevede, zkontroluj, jestli se nevede o balíku. Tři funkce, které se dodávají spolu, mají obvykle tři nezávislé trade-offy a přijmout jeden z nich nezavazuje k ostatním.
U vlastního API se rozhoduj podle toho, jestli daná volba informaci na místo volání přidává, nebo ji z něj odvádí. V Rustu dnes hraje roli pojmenovaných parametrů struktura s pojmenovanými poli nebo builder — ne proto, že je to elegantní, ale proto, že to na místě volání vypíše, co se předává.
A jednu věc si odnes z toho posunu, ne z toho jazyka: pokud dnes většinu tvých volání čte agent, přesunula se hodnota od stručnosti k explicitnosti. Argumenty pojmenuj, výchozí hodnoty nepřidávej.