Podstawy
if w Zero wygląda mniej więcej jak if w każdym innym języku z rodziny C, z jednym istotnym ograniczeniem: warunek musi być typu bool.
Warunek value == 42 jest typu bool, bo == między dwoma wartościami i32 zwraca bool. Każda z dwóch gałęzi zawiera blok instrukcji. Gałąź else jest opcjonalna, ale gdy obie są obecne, obie muszą być blokami { ... }.
Wymóg bool
Niektóre języki pozwalają napisać if value { ... } i traktują 0, "" albo null jako fałsz. Zero nie. Warunek musi być prawdziwym bool.
let count = 0
if count { // błąd kompilacji: oczekiwano bool, otrzymano i32
// ...
}
if count == 0 { // OK
check world.out.write("nic do zrobienia\n")
}
Powód jest ten sam co w całym języku: jawność wygrywa z niejawnością. Agent czytający drugą formę wie dokładnie, co jest sprawdzane. Pierwsza forma pozostawia reguły prawdziwości niejawne, a te różnią się między językami, więc Zero z nich rezygnuje.
Else i else if
Klauzula else wykonuje się, gdy warunek jest fałszywy:
Warunki łączy się przez else if:
if value < 0 {
check world.out.write("ujemna\n")
} else if value == 0 {
check world.out.write("zero\n")
} else {
check world.out.write("dodatnia\n")
}
else if to po prostu else { if ... } bez nawiasów klamrowych: ta sama semantyka, mniej wcięć. Końcowe else jest opcjonalne.
Skąd bierze się warunek
Warunkiem może być wszystko, co daje w wyniku bool:
- Wiązanie typu
bool:if ok { ... }. - Porównanie:
if value == 42 { ... },if x < y { ... }. - Połączenie logiczne:
if a && b { ... },if pair.left == 1_u8 && pair.right == 2_u8 { ... }. - Wywołanie funkcji zwracającej
bool:if isReady(world) { ... }.
Prawdziwy fragment z przykładów języka:
let pair: BytePair = Pair { left: 1_u8, right: 2_u8 }
if pair.left == 1_u8 && pair.right == 2_u8 {
check world.out.write("type alias ok\n")
} else {
check world.out.write("type alias broke\n")
}
&& działa w sposób skrócony, tak jak można się spodziewać: jeśli lewa strona jest false, prawa nie jest obliczana. Tak samo działa ||.
Kiedy użyć match
Łańcuchy if/else if sprawdzają się przy decyzjach na dwie lub trzy strony, zwłaszcza gdy warunki nie mają wspólnej struktury. Ale gdy rozgałęziasz się według wariantów typu sumowego, czyli choice lub enum, właściwym narzędziem jest match.
Porównaj:
// match: wyczerpujące, sprawdzane przez kompilator
match result {
.ok => value { check world.out.write("ok\n") }
.err => message { check world.out.write("err\n") }
}
z:
// if/else na typie sumowym: rozwlekłe i łatwiej o pomyłkę
if result.isOk() {
let value = result.unwrap()
check world.out.write("ok\n")
} else {
check world.out.write("err\n")
}
Forma match czyni analizę wariantów jawną, a kompilator zaprotestuje, jeśli później dojdzie nowy wariant, a zapomnisz go obsłużyć. Forma if po cichu przepuszcza do gałęzi else wszystko, co nie jest ok, nawet zupełnie nowe warianty, o których nikt nie pomyślał.
Zasada: if dla wartości logicznych, match dla wariantów.
Uwagi o stylu
Kilka drobnych konwencji, które ułatwiają czytanie kodu zarówno ludziom, jak i agentom:
- Zawsze używaj nawiasów klamrowych wokół ciała. Nawet jednolinijkowe ciało trafia do
{ ... }. Nie ma formy jednej instrukcji bez nawiasów. To celowe: wcięcia pozostają wiarygodne, a znika częste źródło błędów w rodzinie C. - Najczęstszą gałąź umieszczaj jako pierwszą. Czytelnicy (i agenci) przeglądają kod od góry do dołu.
- Wybieraj warunki pozytywne, gdy są równie jasne.
if isReadyłatwiej zrozumieć niżif !notReady, przy pozostałych czynnikach bez zmian.
Dalej: pętle while
Instrukcje warunkowe decydują, co zrobić; pętle decydują, ile razy to zrobić. Następny artykuł omawia pętle while w Zero, czyli konstrukcję pętli dostępną we wczesnej wersji języka.
Najczęściej zadawane pytania
Jak działają instrukcje if w Zero?
Użyj if condition { ... } z opcjonalną klauzulą else { ... }. Warunek musi być typu bool: Zero nie zamienia liczb całkowitych, tekstów ani innych wartości na wartość logiczną. Przykład: if value == 42 { check world.out.write("yes\n") } else { check world.out.write("no\n") }.
Czy Zero ma operator trójargumentowy?
Zero celowo utrzymuje małą powierzchnię i używa bloków if / else zamiast osobnego operatora condition ? a : b. Oficjalne przykłady wolą jawne gałęzie nawet w krótkich przypadkach. Jeśli język obsługuje if jako wyrażenie, można się spodziewać, że Zero pójdzie w tę stronę, zamiast wprowadzać drugą składnię.
Czy w Zero można łączyć if/else if?
Tak. Łącz warunki, pisząc else if między gałęziami: if a { ... } else if b { ... } else { ... }. Przy bardziej rozbudowanych rozgałęzieniach na choice lub enum sięgnij raczej po match: jest wyczerpujące, a kompilator sprawdza, czy obsłużono każdy wariant.
Dlaczego Zero nie zamienia wartości na bool?
Filozofia projektowa Zero brzmi: 'wszystko jest jawne'. Niejawna prawdziwość, czyli traktowanie 0, "" albo null jako fałszu, to częste źródło błędów i trudno o niej precyzyjnie rozumować agentowi. Wymaganie prawdziwego bool sprawia, że decyzja o rozgałęzieniu jest widoczna w kodzie i łatwiejsza do mechanicznego sprawdzenia.
Kiedy używać match zamiast if/else?
Używaj match, gdy rozgałęziasz się według wariantów choice lub enum. Kompilator może sprawdzić, czy obsłużono każdy przypadek, czego łańcuchy if/else if nie potrafią. Zostaw if/else dla prostych predykatów logicznych: sprawdzeń równości, porównań i krótkich rozgałęzień na dwie strony.