I was hoping for some feedback like this. You're right, I think I could solve it by using an ADT and splitting `apply` into two parts: atom-ops and list-ops.
module LispData = struct
(* eventually support more than just strings as ints *)
type t = Atom of string | List of string list
let create v = (v, if is_atom v then `Atom else `List)
let recover (v, t) : t = if t = `Atom then as_atom v else as_list v
end
let rec apply car cdr = match LispData.recover car with
| atom `Atom -> atom_ops atom cdr
| list `List -> list_ops list cdr
This is what I'm thinking, roughly speaking.