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