Manu

OCaml matching in recursive function

Been exploring ocaml recently and was trying to implement a very basic REPL using recursive functions.

The usual REPL code we write are as below

loop do
 # ...
end

Though in OCaml we usually don’t write imperative code, so trying that with functions would look like

  let rec program_loop () =
  let _ = print_endline "Input proper input" in
    let input = read_line () in
      match input with
      | "add" -> print_endline "Input is add"
      | "print" -> print_endline "Input is print"
      | _ -> print_endline "Invalid input";
      program_loop ()

This code looks pretty intuitive until we look at the match here, there is a bug here. Formatting this snippet we will get the below

let rec program_loop () =
  let _ = print_endline "Input proper input" in
    let input = read_line () in
      match input with
      | "add" -> print_endline "Input is add"
      | "print" -> print_endline "Input is print"
      | _ -> (print_endline "Invalid input");
              program_loop ())

We can see that the program_loop() invocation has gone to the last catch of match. The semicolon does not help here as expected. To make this work what we need to do is

let rec program_loop () =
  let _ = print_endline "Input proper input" in
    let input = read_line () in
      (match input with
      | "add" -> print_endline "Input is add"
      | "print" -> print_endline "Input is print"
      | _ -> print_endline "Invalid input");
      program_loop ()

This is one of the gotchas of match in OCaml. Took me sometime to figure this and had help from the community as well. For a beginner this can be trippy.

The final snippet that I implemented is as below. It’s a simple program which runs and keeps taking input and give results. It’s self expalanatory so I will leave it to the reader to figure out the rest 😄

(*  program.ml *)
let print_and_return_list list =
  let _ = print_endline "List:: " in
  (match list with
   | [] -> print_endline "List is empty"
   | _ -> List.iter (fun x -> print_endline x) list);
  list

let parse_input input list =
  let parts = String.split_on_char ' ' input in
  match parts with
  | ["a"; v] ->  v :: list
  | ["p"] ->  print_and_return_list list
  | ["q"] -> exit 0
  | _ -> (print_endline "Invalid input"; list)

let print_prompt () = Printf.printf ">> "

let rec program_loop master_list =
  let _ = print_prompt () in
  let input = read_line () in
  let new_list = parse_input input master_list in
  program_loop new_list

(* Call the loop function to start the program *)
let _ = print_endline "Welcome to a POC REPL"
let _ = program_loop []

Very clean and concise.