Up to Main Index Up to Journal for July, 2025
JOURNAL FOR TUESDAY 29TH JULY, 2025
______________________________________________________________________________
SUBJECT: A ternary conditional for Mere
DATE: Tue 29 Jul 15:25:55 BST 2025
A feature often requested for my programming language Mere is for a ternary
operator. Initially I wasn’t sure I wanted a ternary operator. It can lead to
crytptic code, especially when nested. Indeed, Go does not have a ternary
operator for this very reason — “used too often to create impenetrably complex
expressions”[1]. However, a simple single instance can be quite elegant:
quote = b==2 ? "to be" "not to be"
Consequently, I have spent ages implementing such an operator. Most of the
time was spent dithering on its form and whether the conditional should be a
relaxed ‘truthy’ test or a strict boolean true/false test. I went for strict.
The ternary operator ‘?’ is a conditional expression. In Mere it has the form:
condition ? (expression1, expression2)
For simple expressions the parentheses and comma can be omitted.
The condition is a boolean value. If the condition evaluates to true then
expression1 is returned, otherwise expression2 is returned. This is equivalent
to the following longhand if-else-fi statement block:
if condition == true
result = expression1
else
result = expression2
fi
Here is a simple example of using the ternary operator:
func display n
println n " item" n==1 ? "" "s"
end
@display 0 // displays "0 items"
@display 1 // displays "1 item"
@display 2 // displays "2 items"
This is equivalent longhand code for the function using an if-else-fi block:
func display n
if n == 1
suffix = ""
else
suffix = "s"
fi
println n " item" suffix
end
A little care has to be taken as Mere does not currently have any sort-circuit
evaluation yet. This means that both expression1 and expression2 are always
evaluated, in that order.
The ternary operators may be nested:
println a == 1 ? "one" a == 2 ? "two" "many"
The ternary operator has right associativity. In the above, assuming a is 10,
‘a == 2 ? "two" "many"’ is evaluated first returning “many”. Then ‘a == 1 ?
"one" "many"’ is evaluated, which also returns “many”.
If there are many nested ternary operators, it can also be written using this
form:
println a == 1 ? ("one",
a == 2 ? ("two",
"many",
))
This can be seen as a little ugly as commas and parenthesis are required to
allow splitting the expressions over multiple lines.
In either case, it is the longhand equivalent of:
if a == 1
println "one"
elif a == 2
println "two"
else
println "many"
fi
As currently implemented, due to there being no short circuit evaluation, all
of the nested ternary operators will be evaluated. A better approach than
deeply nesting ternary operators would be to use if-elif-fi or a table lookup.
When testing with 10 nested ternary operators against a if-elif-fi block, the
if-elif-fi block was about 33% faster. This is mainly due to the if-elif-fi
being able to skip additional processing once a match was found.
The optimizer has a rule for when the condition evaluates to a constant bool.
Borrowing from an example in Go issue #33171, consider the following code
where Production is known at compile time:
const Production = true
const Port = Production ? 80 8080
println "Using port: " Port
This is optimized to the equivalent of:
const Production = true
const Port = 80
println "Using port: " 80
While the conditional is a strict boolean test, a more relaxed ‘truthy’ test
can be achieved using a boolean conversion. In converting to a boolean, zero
values for a type, empty arrays, empty maps and undefined variables all
evaluate to false. For example:
list = ""
range ; item; []string "ant", "bat", "cat"
list += bool list ? ", " ""
list += item
next
println list // displays "ant, bat, cat"
Here the “bool list” evaluates to false if ‘list’ is an empty string, the zero
value for a string, otherwise it evaluates to true. If the list isn’t the
empty string we append “, ” else we append an empty string “”.
The above is only an example, it can be more efficiently written as:
println []string("ant", "bat", "cat") ~j ", "
This is only an initial implementation and I consider it experimental.
--
Diddymus
[1] https://go.dev/doc/faq#Does_Go_have_a_ternary_form
Up to Main Index Up to Journal for July, 2025