15 August 2023
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.