langur

if expressions

if cond1 { ... } [else if condn { ... } ...] [else { ... }]

Curly braces are always required.

The else if and the else sections are optional.

No parentheses are necessary around test conditions.

An if expression can be used inline to create a value.

An if expression always returns a value, even if it's nothing (null).

See also the very expressive and useful given expression.

shortened form if expressions

The shortened form uses parentheses, colons, and semicolons, and no extra keywords.

The shortened form expects a single expression per section rather than a block (is more limited). The else if and the else sections are optional.

if test1 { 1 } else if test2 { 2 } else { 3 } # long form if expression if(test1: 1; test2: 2; 3) # shortened form if expression

This is a semantic convenience and the result is the same as the long form.

if statements

You can use an if statement, in the following form, with the action statement on the same line.

if condition: statement

This is convenient for conditionally using something such as break or next without needing curly braces, but does not allow for else if or else sections (not the same as a Python if statement).

As of 0.8.5, you can use an expression for the "statement." This allows you to write something like the following.

if .i div 2: .v x= 2

truthiness

"Truthiness" determines whether a logical test passes or fails. This includes the result of if expressions, logical operators, and alternate tests in given expressions.

The result of this "truthiness" may differ from the result of the toBool() and toBoolOrNull() functions, which serve a different purpose.

value logical result
true truthy
false not truthy
null not truthy
NaN (prior to 0.8) not truthy
empty array not truthy
empty hash not truthy
other truthy

Below is an example of using truthiness to test if a regex pattern matched anything and then using the results. (The matches() function returns an array of progressive matches, and an empty one if there are none.)

val .results = matches re/abc+/, .checkString if .results { ... }

Now, with assignment decoupling, we can write something like the following.

if val .alias, .name = submatch($re/^(\.idregex;)\\s*;\\s*(\.idregex;)/, .row) { # use .alias and .name here ... }

Note that prior to 0.10, you would use parentheses as follows.

if val (.alias, .name) = submatch($re/^(\.idregex;)\\s*;\\s*(\.idregex;)/, .row) { ... }

scope within if expressions

Each section of an if expression is scoped, as illustrated below.

if ... { val .x = 123; .x + .y } else { val .x = 789; .x + .y } # each .x different, and not seen after if expression

Variables declared within test conditions are available to the action block that goes with the test, but not after it.

if val .matches = submatch(RE/(?i)-(\d+\.\d+\.\d+)\.txt/, .row) { return .matches[1] } else if val .matches = submatch(RE/(?i)-(\w+)\.txt/, .row) { # this .matches different than the .matches in previous section of if expression ... } # neither version of .matches available after if expression

val .x = 123 if true { val .x = 7 # .x == 7 } # .x == 123

var .y = 123 if true { .y = 7 # .y == 7 } # .y == 7 (was mutable and not redeclared)