Za pomocą wyrażenia if
można decydować o zmianie przepływu programu. Podobnie jak w innych językach programowania, w Verse wyrażenie if
służy do wykonywania warunkowego, jednak w przypadku Verse decyzja jest podejmowana na podstawie spełnienia kryteriów powodzenia lub niepowodzenia zadanych w warunkach.
Na przykład można napisać kod, który będzie definiował wysokość, z jakiej gracz może spaść, zanim zacznie odnosić obrażenia.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
# Gracz odnosi obrażenia, jeśli spadnie z wysokości ponad 3 metrów
if (PlayerFallHeight > 3.0):
DealDamage()
# Zresetuj wysokość spadania gracza
ZeroPlayerFallHeight()
W tym przykładzie, jeśli wartość zmiennej PlayerFallHeight
będzie większa niż trzy metry, warunek zostanie spełniony, a wyrażenie DealDamage()
zostanie wykonane, zanim wysokość spadania gracza zostanie zresetowana. W innym przypadku warunek nie zostanie spełniony, więc gracz nie odniesie żadnych obrażeń, choć wysokość jego spadania zostanie zresetowana.
if
Składnia przykładowej wysokości spadania gracza może być następująca:
expression0
if (test-arg-block):
expression1
expression2
Po wykonaniu wyrażenia expression0
program Verse przechodzi do bloku if. Jeśli warunek test-arg-block
zostanie spełniony, program Verse wykona wyrażenie expression1
, które może być pojedynczym wyrażeniem lub blokiem wyrażeń. W innym przypadku, jeśli warunek test-arg-block
nie zostanie spełniony, program Verse pominie wyrażenie expression1
i wykona jedynie wyrażenie expression2
.

Diagram przepływu logiki bloku if.
if ... else
Można również zdefiniować wyrażenie, które zostanie wykonane w przypadku niepowodzenia wyrażenia if
.
Przykładowo gracz powinien zyskać zdolność wykonania podwójnego skoku, jeśli spadnie z wysokości mniejszej niż trzy metry i jego miernik skoku wskazuje 100 procent. Jeśli jednak gracz spadnie z wysokości ponad trzech metrów lub jego miernik skoku nie wskazuje 100 procent, wówczas postać zamacha rękoma, informując gracza, że nie może wykonać podwójnego skoku.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if (PlayerFallHeight < 3.0 and JumpMeter = 100):
# Wykonaj podwójny skok.
ActivateDoubleJump()
# Zresetuj wysokość spadania gracza.
ZeroPlayerFallHeight()
else:
# Zamachaj ramionami postaci, aby dać graczowi znać,
# że nie może wykonać teraz podwójnego skoku!
ActivateFlapArmsAnimation()
# Ustaw odnowienie ponownego skoku, aby szybkie naciskanie przycisku skoku
# nie powodowało nieprawidłowego odtwarzania animacji „machania ramionami”.
SetDoubleJumpCooldown()
W tym przykładzie warunek if
ocenia, czy wysokość PlayerFallHeight
jest mniejsza niż trzy metry oraz czy miernik JumpMeter
wskazuje wartość równą 100 procent. Jeśli warunek zostanie spełniony, przed wyrażeniem SetDoubleJumpCooldown()
zostaną wykonane wyrażenia ActivateDoubleJump()
i ZeroPlayerFallHeight()
.
Jeśli warunek if
nie zostanie spełniony, wówczas przed wyrażeniem SetDoubleJumpCooldown()
zostanie wykonane wyrażenie ActivateFlapArmsAnimation()
następujące po członie else
.
Pod względem składniowym przykład wyrażeń if-else wygląda następująco:
expression0
if (test-arg-block):
expression1
else:
expression2
expression3

Diagram przepływu logiki bloku if-else.
if ... else if ... else
Jeśli gracz ma 100-procentowe osłony w momencie upadku z wysokości ponad trzech metrów, powinien odnieść maksymalne obrażenia, ale mimo to przeżyć. Zmodyfikujmy regułę, która daje graczom zdolność podwójnego skoku, w taki sposób, aby gracze zyskiwali możliwość wykonania podwójnego skoku, jeśli spadną z wysokości mniejszej niż trzy metry, a ich miernik skoku wskazuje powyżej 75 procent.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if (PlayerFallHeight > 3.0 and shields = 100):
DealMaximalDamage()
return false
else if (PlayerFallHeight < 3.0 and JumpMeter > 75):
ActivateDoubleJump()
return false
else:
return true
# Zresetuj wysokość spadania gracza
ZeroPlayerFallHeight()
Pod względem składniowym przykład wyrażeń if-else if-else wygląda następująco:
expression0
if (test-arg-block0):
expression1
else if (test-arg-block1):
expression2
else:
expression3
expression4

Diagram przepływu logiki bloku if-else if-else.
if ... then
Każdy z warunków if
z poprzednich przykładów można zapisać w wielu wierszach i nie wpłynie to na sposób ich działania:
expression0
if:
test-arg-block
then:
expression1
expression2
Blok kodu test-arg-block
może zawierać co najmniej jeden wiersz warunków, przy czym wszystkie one muszą zostać spełnione, aby przed wyrażeniem expression2
zostało wykonane wyrażenie expression1
. W przeciwnym razie zostanie wykonane jedynie wyrażenie expression2
.
Przepisany w tym formacie przykład z sekcji if ... else wygląda następująco:
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if:
PlayerFallHeight < 3.0
JumpMeter = 100
then:
# Wykonaj podwójny skok.
ActivateDoubleJump()
# Zresetuj wysokość spadania gracza.
ZeroPlayerFallHeight()
else:
# Zamachaj ramionami postaci, aby dać graczowi znać,
# że nie może wykonać teraz podwójnego skoku!
ActivateFlapArmsAnimation()
# Ustaw odnowienie ponownego skoku, aby szybkie naciskanie przycisku skoku
# nie powodowało nieprawidłowego odtwarzania animacji „machania ramionami”.
SetDoubleJumpCooldown()
Wyrażenie jednowierszowe
Wyrażenie if else
można zapisać jako wyrażenie jednowierszowe, podobnie jak operatory trójargumentowe w innych językach programowania. Na przykład, aby przypisać maksymalną lub minimalną wartość dla Recharge
w oparciu o atrybut ShieldLevel
, można użyć następującego kodu Verse:
Recharge : int = if(ShieldLevel < 50) then GetMaxRecharge() else GetMinRecharge()
Wymagania dotyczące predykatów
Predykat if
, czyli wyrażenie w nawiasie (), działa inaczej niż w innych językach programowania w tym sensie, że nie zwraca wartości logicznej (nazywanej logic
w Verse). Zamiast tego predykat ma efekt decides
(zauważ, że choć przy tworzeniu podtypów zazwyczaj dopuszcza się podzbiór efektów w miejscach, gdzie dopuszczalny jest zbiór efektów, if wymaga, aby efekt ogólny predykatu zawierał człon decides
). Efekt zostaje usunięty z otaczającego zakresu. Chodzi o to, że efekt decides
ze wszystkich operacji w predykacie if
jest wykorzystywany przez konstrukcję if
. Na przykład w poniższym kodzie Main
nie zawiera efektu decides
, choć wywołuje Foo
, który go zawiera.
Foo()<transacts><decides> : void = {}
Bar() : void = {}
Main() : void =
if (Foo[]):
Bar()
Dzieje się tak dlatego, że zamiast użycia wartości wejściowej logic
w if
do wskazania wybranej gałęzi, to powodzenie operacji zawartych w predykacie if
jest podstawą decyzji o wyborze gałęzi – gałęzi then
w przypadku powodzenia wszystkich operacji lub gałęzi else
(o ile została zastosowana) w razie niepowodzenia dowolnej z operacji. Zauważ, że oznacza to możliwość zastosowania dowolnych operacji w predykacie if
, w tym możliwość wprowadzenia stałych. Na przykład:
Main(X : int) : void =
Y = array{1, 2, 3}
if:
Z0 := Y[X]
Z1 := Y[X + 1]
then:
Use(Z0)
Use(Z1)
Innymi słowy, zakres gałęzi then
obejmuje dowolne nazwy wprowadzone w predykacie if
.
Zachowanie transakcyjne
Kolejną różnicą w zastosowaniu if
w porównaniu z innymi językami programowania jest zachowanie transakcyjne predykatu if
. Predykat if
nie może zawierać efektu no_rollback
(używanego w sposób domniemany przez wszystkie funkcje, które nie zawierają jawnie wskazanego efektu transacts
, varies
lub computes
). Wynika to z tego, że w razie niepowodzenia predykatu wszystkie operacje podjęte w trakcie wykonywania predykatu (z wyjątkiem wszelkich operacji wpływających na zasoby poza środowiskiem uruchomieniowym, takich jak operacje wejścia/wyjścia pliku bądź zapis do konsoli) zostaną cofnięte przed wykonaniem gałęzi else
. Na przykład:
int_ref := class:
var Contents : int
Incr(X : int_ref)<transacts> : void =
set X.Contents += 1
Foo(X : int) : int =
Y := int_ref{Contents := 0}
if:
Incr(Y)
X > 0
then:
Y.Contents
else:
Y.Contents
Funkcja Foo(-1)
zwróci 0
, natomiast funkcja Foo(1)
zwróci 1
. Dzieje się tak dlatego, że choć wywołanie do Incr
występuje przed testem X > 0
, powodowana przez nie mutacja Y
zostaje cofnięta przed wykonaniem gałęzi else
. Pamiętaj, że Incr
musi ręcznie wskazywać efekt transacts
. Domyślnie zachowanie transakcyjne wskazywane przez domniemany efekt no_rollback
nie ma zastosowania, jednak można je dodać poprzez ręczne zdefiniowanie efektu transacts
(zastąpienie domniemanego efektu no_rollback
).