sortingocamltail-recursion

Tail-recursive merge sort in OCaml


I’m trying to implement a tail-recursive list-sorting function in OCaml, and I’ve come up with the following code:

let tailrec_merge_sort l =
  let split l = 
    let rec _split source left right =
      match source with
        | [] -> (left, right)
        | head :: tail -> _split tail right (head :: left) 
    in _split l [] []
  in

  let merge l1 l2 = 
    let rec _merge l1 l2 result =
      match l1, l2 with
        | [], [] -> result
        | [], h :: t | h :: t, [] -> _merge [] t (h :: result)
        | h1 :: t1, h2 :: t2 ->
            if h1 < h2 then _merge t1 l2 (h1 :: result)
            else            _merge l1 t2 (h2 :: result)
    in List.rev (_merge l1 l2 [])
  in

  let rec sort = function
    | [] -> []
    | [a] -> [a]
    | list -> let left, right = split list in merge (sort left) (sort right)
  in sort l
;;

Yet it seems that it is not actually tail-recursive, since I encounter a "Stack overflow during evaluation (looping recursion?)" error.

Could you please help me spot the non tail-recursive call in this code? I've searched quite a lot, without finding it. Cout it be the let binding in the sort function?


Solution

  • The expression

    merge (sort left) (sort right)
    

    is not tail-recursive; it calls both (sort left) and (sort right) recursively while there is remaining work in the call (merge).

    I think you can fix it with an extra continuation parameter:

    let rec sort l k =
      match l with
      | [] -> k [] 
      | [a] -> k [a] 
      | list -> 
          let left, right = split list in 
          sort left (fun leftR -> 
            sort right (fun rightR -> 
              k (merge leftR rightR)))
    in 
    sort l (fun x -> x)