WJH

Posted on

# User-defined Ternary Operator

## Introduction

Ternary operator is a very useful syntactical notation
for condensing meaning. Basically it takes three
arguments, and syntactically speaking it's identifier is
split into two parts.

For example:

1. Javascript conditionals `a ? b : c`
2. Math range check `a < b < c`
3. SQL joins `a JOIN b ON c`

This is similar to why we
prefer infix binary operators (IBO), because they can be
chained together easily (or so called
associativity).

For example, most if not all of us will think that A is easier to read and understand than B:

A B
`a + b + c` `(+ (+ a b) c)`

Because IBO are so important,
most functional programming (FP) languages like Haskell
allow users to define custom IBO.

For example:

``````-- pipe forward operator
(|>) :: a -> (a -> b) -> b
a |> f = f a

-- example usage
main = [1, 2, 3]
|> map (+2)
|> filter (>3)
|> print
|> putStrLn -- [4, 5]
``````

See more at Haskell Infix Operator.

## Emulating with binary operator

However, when it comes to user-defined ternary operator,
none of the programming languages provide the mechanism.

Although ternary operator can be emulated using double
IBO, it is not without its own caveats.

``````data Cond a = a :? a

infixl 0 ?
infixl 1 :?

(?) :: Bool -> Cond a -> a
True  ? (x :? _) = x
False ? (_ :? y) = y

test = 1 < 2 ? "Yes" :? "No"
``````

The caveat of such emulation is that partial usage is
not only syntactically valid, but also
semantically valid.

For example, we can omit that `:?`
part, and the compiler will not complain.

``````x = 1 < 2 ? "Yes"
``````

From the user experience (UX) perspective, this is very bad,
because we intended users to always use `?` together with
`?:`.

## Emulating using mixfix operators

Other than using IBO, we can also emulate ternary operators using mixfix operators.

For example, in Agda:

``````-- Example function name _if_else_
-- (emulating Python's conditional operator)
_if_else_ : {A : Set} -> Bool -> A -> A -> A
x if true else y = x
x if false else y = y
``````

The caveat of such approach is that users will be
required to lookup the syntax before they can even parse
a piece of code that is filled with mixfix operators.

Because, for example, the above `_if_else_` operators can also be defined as:

``````if_then_else_ : {A : Set} -> Bool -> A -> A -> A
if x then true else y = x
if x then false else y = y
``````

In this case, the user will not be able to parse the following code correctly without knowing the syntax of `if` or/and `else`:

``````x = a if b else c -- is this correct?
y = if a then b else c -- or this?
``````

## The Question

Due to the aforementioned caveats of emulating ternary
operators, I am on a quest for searching a mechanism to allow
user-definer ternary operators that is:

1. Unambiguous (parsable by both machine and human)
2. Universal (can be applied to all kinds of ternary operators)

## Inspiration

I have been contemplating this question for a while, but
still there is no significant progress, until I read the
book The Relational Model of Database
Management

by the inventor of Relational Algebra, Edgar. F. Codd.

His notation for denoting theta-joins is ingenious.

For example,

SQL Edgar's notation
`X join Y on A join Z on B` `X [A] Y [B] Z`

operator as decorated binary operators!

In the above example, the binary operator `[]`, is
decorated/tainted with `A`, giving it a different
meaning.

This actually also relates to the mathematical notation of theta-join:

In this case, thetha symbol is decorating the join
symbol, turning it into a ternary operator that still
behave as a binary operator.

The advantage of this approach is that ternary
operators behave like binary operators, which means
that they can be chained together naturally.

By expanding on the idea where ternary operator are just
decorated/tainted binary operators, I first have this idea:

Ternary operators can be defined using the following syntax:

``````a X[ b ]Y c
``````

Where `a`, `b` and `c` are the arguments, and `X[` and
`]Y` are together the name of the ternary operators.

For example, let's define range-check:

``````-- Definition
a <[ b ]< c = a < b && b < c

-- Usage
print (1 <[2]< 3) -- true
print (1 <[3]< 2) -- false
``````

Assuming `[` `]` is not used anywhere in the syntax of
the language, such ternary operators usage can be parsed
easily. Because whenever the user or machine sees `[` or
`]`, then they can anticipate a ternary operator.

However, the above syntax is obviously too noisy, so to
reduce the noise, we can swap the square brackets `[`
`]`, with one of the most invisible ASCII operator, the backtick.

With this modification, the above range-check can be rewritten as follows:

``````-- Definition
a <` b `< c = a < b && b < c

-- Usage
print (1 <` 2 `< 3) -- true
print (1 <` 3 `< 2) -- true
``````

Thus, the best mechanism that I can think of so far to define ternary operator is as follows:

``````a X` b `Y c

-- Where a, b and c are the arguments
-- while X` and `Y together is the name of the ternary operator
``````

Also regarding precedence, ternary operators should have
lower precedence than binary operators, for example:

``````(a <` b + c `< d) MEANS (a <` (b + c) `< d)
``````

Regarding, associativity, I will prefer
right-associativity over left-associativity, since it is
more common in most cases.

Therefore:

``````a X` b `Y c X` d `Y e = a X` b `Y (c X` d `Y e)
``````

For example, the conditional ternary operator:

``````true then` b `else c = b
false then` b `else c = c
``````

Where:

``````a then` b `else c then` d `else e
``````

Should conventionally mean (right-associative):

``````a then` b `else (c then` d `else e)
``````

Rather than (left-associative):

``````(a then` b `else c) then` d `else e
``````