How it works

Ambifix notation

In ambifix notation, code may seem similar to infix notation, except with added 'ending' operators, which are similar to postfix notation operators. Here's the same example in all three notations:

Infix Ambifix Postfix
(5 + 3) * 5 - 3 5 (+ 3 +) (* 5 *) (- 3 -) 5 3 + 5 * 3 -

You are probably most familiar with the first of the three expressions, it's written in infix. Infix notation has operators with different precedences, which means that some operators are to be evaluated before others. Operators can be "grouped" with some operands by parentheses, which makes them have the highest precedence. For example, normally a + operation is evaluated after a * operation, but in this case it (5 + 3) will be evaluated before evaluating the * operation ((5 + 3) * 5). Sounds complicated, and can therefore be hard to grasp, and it's easy to make mistakes.

Though you may have never seen ambifix notation before, you might already have a made guess how it works. Essentially you just evaluate all operations from left to right, starting with 5, adding 3, multiplying by 5 and subtracting 3. You don't even have to think about the order... but what about the extra operators with the closing parenthesis at the end, what is that needed for? That's where postfix comes in...

In postfix, there are no parentheses. They are not needed, because postfix notation can be seen as a way of writing down all of the consecutive operations you need to perform to get the result, or results even. You see, 5 3 means 'remember 5 and 3'. Now + means 'now remember the result of the addition of the last two items instead'. Now there is only 8 left to remember, but then you append '5', so now the items are 8 and 5. And so forth, no parentheses needed! The items in the postfix notation (including literals such as 5 and 3) are all called operators, because they all append and modify items at the end of the stack.

Let's get back to the question: why are there both 'opening' and 'closing' operators in ambifix notation. Actually there are three reasons, they are (in no particular order):

  1. It lets the reader of the code choose how they want to read the code; infix or postfix. Infix notation is in most widespread use, so it's useful to be able to read it like that, but some people (including me) like to read, think and write in postfix, because it's simply the order of evaluation. So with code written in ambifix you get the best of both worlds. Plus, the level of the parentheses (most of the time) represents how many items are on the operating stack at any time. Example:
    [( Operations )]                  [( #Items  Stack      )]
       5                              [(      1  [ 5      ] )]
           (+                         [(      2  [ 5 4    ] )]
               4                      [(      -  -          )]
                   (*                 [(      3  [ 5 4 10 ] )]
                       10             [(      -  -          )]
                           *)         [(      2  [ 5 40   ] )]
                               +)     [(      1  [ 45     ] )]
  2. You make less mistakes, because there is less need for groupings and because it makes the writer of the code literally think twice about the operator when typing the operation.
  3. It shows you the flow of the data. Anywhere you see an item outside of parentheses, it means that it's a new item/literal, which will be modified by the upcoming operation(s). Example:
    [ 5 (+ 4 +) (* 10 *) 32 (* 4 *) (/ 64 /) ]
    Quickly scanning the code, you'll be able to find that the result is an array containing two items, more specifically, the array is [ 90 2 ].

Page rendered on 22-06-2021 at 14:19:17, it took 0.222427 seconds