Author: Christian.Queinnec@lip6.fr

## Desktop Calculator

To understand how a program is built in Objective CAML, it is necessary to develop one. The chosen example is a desktop calculator---that is, the simplest model, which only works on whole numbers and only carries out the four standard arithmetic operations.
To begin, we define the type key to represent the keys of a pocket calculator. The latter has fifteen keys, namely: one for each operation, one for each digit, and the `=` key.

    # type key = Plus | Minus | Times | Div | Equals | Digit of int ;;

We note that the numeric keys are gathered under a single constructor Digit taking an integer argument. In fact, some values of type key don't actually represent a key. For example, `(`Digit`` `3` `2``)` is a possible value of type key, but doesn't represent any of the calculator's keys.
So we write a function `valid` which verifies that its argument corresponds to a calculator key. The type of this function is key -> bool, that is, it takes a value of type key as argument and returns a value of type bool.
The first step is to define a function which verifies that an integer is included between 0 and 9. We declare this function under the name `is_digit`:

    # let is_digit = function x -> (x>=0) && (x<=9) ;;
    val is_digit : int -> bool = <fun>

We then define the function `valid` by pattern-matching over its argument of type key:

    #   let valid ky = match ky with
         Digit n -> is_digit n
       | _ -> true ;;
    val valid : key -> bool = <fun>

The first pattern is applied when the argument of `valid` is a value made with the Digit constructor; in this case, the argument of Digit is tested by the function `is_digit`. The second pattern is applied to every other kind of value of type key. Recall that thanks to typing, the value being matched is necessarily of type key.
Before setting out to code the calculator mechanism, we will specify a model allowing us to describe from a formal point of view the reaction to the activation of one of the device's keys. We will consider a pocket calculator to have four registers in which are stored respectively the last computation done, the last key activated, the last operator activated, and the number printed on the screen. The set of these four registers is called the state of the calculator; it is modified by each keypress on the keypad. This modification is called a transition and the theory governing this kind of mechanism is that of automata. A state will be represented in our program by a record type:

    # type state = {
       lcd : int; (* last computation done   *)
       lka : key; (* last key activated      *)
       loa : key; (* last operator activated *)
       vpr : int  (* value printed           *)
     } ;;

Figure 2.6 gives an example of a sequence of transitions.

> | state | key
> | (0,=,=,0) | 3
> ¾® | (0,3,=,3) | +
> ¾® | (3,+,+,3) | 2
> ¾® | (3,2,+,2) | 1
> ¾® | (3,1,+,21) | ×
> ¾® | (24,*,*,24) | 2
> ¾® | (24,2,*,2) | =
> ¾® | (48,=,=,48) |

> Figure 2.6: Transitions for 3+21*2= .

In what follows we need the function `evaluate` which takes two integers and a value of type `key` containing an operator and which returns the result of the operation corresponding to the key, applied to the integers. This function is defined by pattern-matching over its last argument, of type key:

    # let evaluate x y ky = match ky with
         Plus   -> x + y
       | Minus  -> x - y
       | Times  -> x * y
       | Div    -> x / y
       | Equals  -> y
       | Digit _ -> failwith "evaluate : no op";;
    val evaluate : int -> int -> key -> int = <fun>

Now we give the definition of the transition function by enumerating all possible cases. We assume that the current state is the quadruplet (a,b,Å,d):

- a key with digit x is pressed, then there are two cases to consider:
  - the last key pressed was also a digit. So it is a number which the user of the pocket calculator is in the midst of entering; consequently the digit x must be affixed to the printed value, i.e., replacing it with d×10+x. The new state is:
    (a,`(Digit x)`,Å,d×10+x)
  - the last key pressed was not a digit. So it is the start of a new number which is being entered. The new state is:
    (a,`(Digit x)`,Å,x)
- a key with operator Ä has been pressed, the second operand of the operation has thus been completely entered and the calculator has to deal with carrying out this operation. It is to this end that the last operation (here Å) is stored. The new state is:
  (Å d,Ä,Ä,a Å d)

To write the function `transition`, it suffices to translate the preceding definition word for word into Objective CAML: the definition by cases becomes a definition by pattern-matching over the key passed as an argument. The case of a key, which itself is made up of two cases, is handled by the local function `digit_transition` by pattern-matching over the last key activated.

    # let transition st ky =
       let digit_transition n = function
           Digit _ -> { st with lka=ky; vpr=st.vpr*10+n }
         |  _      -> { st with lka=ky; vpr=n }
       in
          match ky with
              Digit p  ->  digit_transition p st.lka
            |  _         ->  let res = evaluate st.lcd st.vpr st.loa
                             in { lcd=res; lka=ky; loa=ky; vpr=res }  ;;
    val transition : state -> key -> state = <fun>

This function takes a state and a key and computes the new state.
We can now test this program on the previous example:

    # let initial_state = { lcd=0; lka=Equals; loa=Equals; vpr=0 } ;;
    val initial_state : state = {lcd=0; lka=Equals; loa=Equals; vpr=0}
    # let state2 = transition initial_state (Digit 3) ;;
    val state2 : state = {lcd=0; lka=Digit 3; loa=Equals; vpr=3}
    # let state3 = transition state2 Plus ;;
    val state3 : state = {lcd=3; lka=Plus; loa=Plus; vpr=3}
    # let state4 = transition state3 (Digit 2) ;;
    val state4 : state = {lcd=3; lka=Digit 2; loa=Plus; vpr=2}
    # let state5 = transition state4 (Digit 1) ;;
    val state5 : state = {lcd=3; lka=Digit 1; loa=Plus; vpr=21}
    # let state6 = transition state5 Times ;;
    val state6 : state = {lcd=24; lka=Times; loa=Times; vpr=24}
    # let state7 = transition state6 (Digit 2) ;;
    val state7 : state = {lcd=24; lka=Digit 2; loa=Times; vpr=2}
    # let state8 = transition state7 Equals ;;
    val state8 : state = {lcd=48; lka=Equals; loa=Equals; vpr=48}

This run can be written in a more concise way using a function applying a sequence of transitions corresponding to a list of keys passed as an argument.

    # let transition_list st ls = List.fold_left transition st ls ;;
    val transition_list : state -> key list -> state = <fun>
    # let example = [ Digit 3; Plus; Digit 2; Digit 1; Times; Digit 2; Equals ]
     in transition_list initial_state example ;;
    - : state = {lcd=48; lka=Equals; loa=Equals; vpr=48}