Topological Sort of Packages
Mark Elvers
1 min read

Categories

  • OCaml

Tags

  • tunbury.org

Given a list of packages and their dependencies, what order should those packages be installed in?

The above graph gives a simple example of the dependencies of the package dune nicely ordered right to left.

We might choose to model this in OCaml using a map with the package name as the key and a set of the dependent packages:

module PackageSet = Set.Make (String);;
module PackageMap = Map.Make (String);;

Thus, the dune example could be defined like this.

let dune = PackageMap.(empty |>
    add "ocaml" (PackageSet.(empty |> add "ocaml-config" |> add "ocaml-variants")) |>
    add "ocaml-config" (PackageSet.(empty |> add "ocaml-variants")) |>
    add "dune" (PackageSet.(empty |> add "ocaml" |> add "base-unix.base" |> add "base-threads.base")) |>
    add "ocaml-variants" (PackageSet.empty) |>
    add "base-unix.base" (PackageSet.empty) |>
    add "base-threads.base" (PackageSet.empty)
  );;

We can create a topological sort by first choosing any package with an empty set of dependencies. This package should then be removed from the map of packages and also removed as a dependency from any of the sets. This can be written concisely in OCaml

let rec topological_sort pkgs =
  match PackageMap.is_empty pkgs with
  | true -> []
  | false ->
      let installable = PackageMap.filter (fun _ deps -> PackageSet.is_empty deps) pkgs in
      let () = assert (not (PackageMap.is_empty installable)) in
      let i = PackageMap.choose installable |> fst in
      let pkgs = PackageMap.remove i pkgs |> PackageMap.map (fun deps -> PackageSet.remove i deps) in
      i :: topological_sort pkgs

This gives us the correct installation order:

# topological_sort dune;;
- : PackageMap.key list =
["base-threads.base"; "base-unix.base"; "ocaml-variants"; "ocaml-config"; "ocaml"; "dune"]