Author: Christian.Queinnec@lip6.fr

## Calculator With Memory

We now reuse the calculator example described in the preceding chapter, but this time we give it a user interface, which makes our program more usable as a desktop calculator. This loop allows entering operations directly and seeing results displayed without having to explicitly apply a transition function for each keypress.
We attach four new keys: C, which resets the display to zero, M, which memorizes a result, m, which recalls this memory and OFF, which turns off the calculator. This corresponds to the following type:

    # type key = Plus | Minus | Times | Div | Equals | Digit of int
              | Store | Recall | Clear | Off ;;

It is necessary to define a translation function from characters typed on the keyboard to values of type key. The exception `Invalid_key` handles the case of characters that do not represent any key of the calculator. The function `code` of module `Char` translates a character to its ASCII-code.

    # exception Invalid_key ;;
    exception Invalid_key
    # let translation c = match c with
         '+' -> Plus
       | '-' -> Minus
       | '*' -> Times
       | '/' -> Div
       | '=' -> Equals
       | 'C' | 'c' -> Clear
       | 'M' -> Store
       | 'm' -> Recall
       | 'o' | 'O' -> Off
       | '0'..'9' as c -> Digit ((Char.code c) - (Char.code '0'))
       | _ -> raise Invalid_key ;;
    val translation : char -> key = <fun>

In imperative style, the translation function does not calculate a new state, but physically modifies the state of the calculator. Therefore, it is necessary to redefine the type `state` such that the fields are modifiable. Finally, we define the exception `Key_off` for treating the activation of the key OFF.

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

    # exception Key_off ;;
    exception Key_off
    # let transition s key = match key with
         Clear ->   s.vpr <- 0
       | Digit n -> s.vpr <- ( if s.lka then s.vpr*10+n else n );
                    s.lka <- true
       | Store  ->  s.lka <- false ;
                    s.mem <- s.vpr
       | Recall ->  s.lka <- false ;
                    s.vpr <- s.mem
       | Off -> raise Key_off
       |  _  -> let lcd = match s.loa with
                             Plus  -> s.lcd + s.vpr
                           | Minus -> s.lcd - s.vpr
                           | Times  -> s.lcd * s.vpr
                           | Div   -> s.lcd / s.vpr
                           | Equals  -> s.vpr
                           | _ -> failwith "transition: impossible match"
                       in
                  s.lcd <- lcd ;
                  s.lka <- false ;
                  s.loa <- key ;
                  s.vpr <- s.lcd;;
    val transition : state -> key -> unit = <fun>

We define the function `go`, which starts the calculator. Its return value is `()`, because we are only concerned about effects produced by the execution on the environment (start/end, modification of state). Its argument is also the constant `()`, because the calculator is autonomous (it defines its own initial state) and interactive (the arguments of the computation are entered on the keyboard as required). The transitions are performed within an infinite loop (while true do) so we can quit with the exception `Key_off`.

    # let go () =
       let state = { lcd=0; lka=false; loa=Equals; vpr=0; mem=0 }
       in try
            while true do
              try
                let input = translation (input_char stdin)
                in transition state input ;
                   print_newline () ;
                   print_string "result: " ;
                   print_int state.vpr ;
                   print_newline ()
              with
                Invalid_key -> ()  (* no effect *)
            done
          with
            Key_off -> () ;;
    val go : unit -> unit = <fun>

We note that the initial state must be either passed as a parameter or declared locally within the function `go`, because it needs to be initialized at every application of this function. If we had used a value `initial_state` as in the functional program, the calculator would start in the same state as the one it had when it was terminated. This would make it difficult to use two calculators in the same program.