The ternary (conditional) operator takes three operands: a condition followed by a question mark (?), then an expression to execute (consequent) if the condition is true followed by a colon (:), and finally the expression to execute (alternate) if the condition is false. This operator is frequently used as a shorthand version of an if statement. It can be used in assignments or any place where an expression is valid. Due to ambiguity in the brightscript syntax, ternary operators cannot be used as standalone statements. See the No standalone statements section for more information.
The optional chaining operator was added to the BrightScript runtime in Roku OS 11, which introduced a slight limitation to the BrighterScript ternary operator. As such, all ternary expressions must have a space to the right of the question mark when followed by [ or (. See the optional chaning section for more information.
The basic syntax is: <condition> ? <value if true> : <value if false>
.
Here's an example:
authStatus = user2 <> invalid ? "logged in" : "not logged in"
transpiles to:
if user2 <> invalid then
authStatus = "logged in"
else
authStatus = "not logged in"
end if
As you can see, when used in the right-hand-side of an assignment, brighterscript transpiles the ternary expression into a simple if else
block.
The bslib_ternary
function checks the condition, and returns either the consequent or alternate. In these optimal situations, brighterscript can generate the most efficient code possible which is equivalent to what you'd write yourself without access to the ternary expression.
This also works for nested ternary expressions:
result = true ? (true ? "one" : "two") : "three"
transpiles to:
if true then
if true then
result = "one"
else
result = "two"
end if
else
result = "three"
end if
Ternary can also be used in any location where expressions are supported, such as function calls or within array or associative array declarations. In some situations it's much more difficult to convert the ternary expression into an if else
statement, so we need to leverage the brighterscript bslib_ternary
library function instead. Consider the following example:
printAuthStatus(user2 <> invalid ? "logged in" : "not logged in")
transpiles to:
printAuthStatus(bslib_ternary(user2 <> invalid, "logged in", "not logged in"))
The bslib_ternary
function checks the condition, and returns either the consequent or alternate.
There are some important implications to consider for code execution order and side effects. Since both the consequent and alternate must be passed into the bslib_ternary
function, this means that both the consequent and alternate will be executed, which is certainly not what most developers intend.
Consider:
printUsername(user = invalid ? "no name" : user.name)
View the transpiled BrightScript code
printUsername((function(__bsCondition, user)
if __bsCondition then
return "no name"
else
return user.name
end if
end function)(user = invalid, user))
If we were to transpile this to leverage the bslib_ternary
function, it would crash at runtime because user.name
will be evaluated even when user
is invalid
. To avoid this problem the transpiler provides conditional scope protection, as discussed in the following section.
Sometimes BrighterScript needs to protect against unintended performance hits. There are 3 possible ways that your code can be transpiled:
In this situation, brighterscript can safely convert the ternary expression into an if else
block. This is typically available when ternary is used in assignments.
m.username = m.user <> invalid ? m.user.name : invalid
transpiles to:
if m.user <> invalid then
m.username = m.user.name
else
m.username = invalid
end if
In this situation, BrighterScript has determined that both the consequent and the alternate are side-effect free and will not cause rendezvous or cloning, but the code was too complex to safely transpile to an if else
block so we will leverage the bslib_ternary
function instead.
printAuthStatus(user = invalid ? "not logged in" : "logged in")
transpiles to:
printAuthStatus(bslib_ternary(user = invalid, "not logged in", "logged in"))
In this situation, BrighterScript has detected that your ternary operation will have side-effects or could possibly result in a rendezvous, and could not be transpiled to an if else
block. BrighterScript will create an immediately-invoked-function-expression to capture all of the referenced local variables. This is in order to only execute the consequent if the condition is true, and only execute the alternate if the condition is false.
printUsername(user = invalid ? "no name" : user.name)
transpiles to:
printUsername((function(__bsCondition, user)
if __bsCondition then
return "no name"
else
return user.name
end if
end function)(user = invalid, user))
printMessage(user = invalid ? getNoNameMessage(m.config) : user.name + m.accountType)
transpiles to:
printMessage((function(__bsCondition, getNoNameMessage, m, user)
if __bsCondition then
return getNoNameMessage(m.config)
else
return user.name + m.accountType
end if
end function)(user = invalid, getNoNameMessage, m, user))
The scope protection works for multiple levels as well
m.count = 1
m.increment = function ()
m.count++
return m.count
end function
printResult((m.increment() = 1 ? m.increment() : -1) = -1 ? m.increment(): -1)
transpiles to:
m.count = 1
m.increment = function()
m.count++
return m.count
end function
printResult((function(__bsCondition, m)
if __bsCondition then
return m.increment()
else
return -1
end if
end function)(((function(__bsCondition, m)
if __bsCondition then
return m.increment()
else
return -1
end if
end function)(m.increment() = 1, m)) = -1, m))
The following library functions are called in the transpiled code:
function bslib_ternary(isTrue, trueValue, falseValue)
if isTrue then
return trueValue
else
return falseValue
end if
end function
The ternary operator may only be used in expressions, and may not be used in standalone statements because the BrightScript grammer uses =
for both assignments (a = b
) and conditions (if a = b
)
' this is generally not valid
condition ? doSomething() : orElse()
For example:
a = myValue ? "a" : "b"
This expression can be interpreted in two completely separate ways:
'assignment
a = (myValue ? "a" : "b'")
'ternary
(a = myValue) ? "a" : "b"
This ambiguity is why BrighterScript does not allow for standalone ternary statements.
The optional chaining operator was added to the BrightScript runtime in Roku OS 11, which introduced a slight limitation to the BrighterScript ternary operator. As such, all ternary expressions must have a space to the right of the question mark when followed by [
or (
. If there's no space, then it's optional chaining.
For example:
Ternary:
data = isTrue ? ["key"] : getFalseData()
data = isTrue ? (1 + 2) : getFalseData()
Optional chaining:
data = isTrue ?["key"] : getFalseData()
data = isTrue ?(1 + 2) : getFalseData()
The colon symbol :
can be used in BrightScript to include multiple statements on a single line. So, let's look at the first ternary statement again.
data = isTrue ? ["key"] : getFalseData()
This can be logically rewritten as:
if isTrue then
data = ["key"]
else
data = getFalseData()
Now consider the first optional chaining example:
data = isTrue ?["key"] : getFalseData()
This can be logically rewritten as:
data = isTrue ?["key"]
getFalseData()
Both examples have valid use cases, so just remember that a single space could result in significantly different code output.