{Record sort_by_dependence gather_imports}

Where

Let (sort_by_dependence packages)
    Do (LIST.for_each packages
        Func p.
            (LIST.for_each p.imports
                Func path.
                    If (STRING.equal path p.path)
                        (OS.die (STRING.concat ["Package " path " imports itself."]))
                        {}))
    Let ordered_paths
        Let components
            Let MAP (SEARCH.MAP STRING.compare (Func {key _}. key))
            In
            Let G (GRAPH.G MAP)
            Let g (MAP.new (LIST.map packages (Func p. {p.path p.imports})))
            In
            (G.strongly_connected_components g)
        In
        (LIST.map components
            Func c.
                Match c
                | `cons.{path paths}
                    Match paths
                    | `nil path
                    | `cons._
                        (OS.die "There is a cycle in the package dependence graph.")
                    ;
                ;)
    In
    (LIST.map ordered_paths
        Func path.
            Let (has_matching_path package)
                (STRING.equal package.path path)
            In
            Match (LIST.filter packages has_matching_path)
            | `cons.{package _} package
            ;)

Let (gather_imports expr)
    Let SET (SEARCH.SET STRING.compare)
    In
    Let empty (Func set. set)
    Let (reduce map exprs)
        Func set.
            (LIST.reduce exprs set
                Func set expr. For (f set) Let f (map expr))
    In
    Define (map expr)
        Match expr
        | `true empty
        | `false empty
        | `num._ empty
        | `str._ empty
        | `package.path (Func set. (SET.insert set path))
        | `prim._ empty
        | `var._ empty
        | `chain.{expr _} (map expr)
        | `tuple.exprs (reduce map exprs)
        | `record.{_ inits} (reduce map inits)
        | `block.{binders expr}
            (reduce map
                (LIST.cons expr
                    (LIST.map binders
                        Func binder.
                            Match binder
                            | `let.{_ expr} expr
                            | `do.expr expr
                            ;)))
        | `app.{func args} (reduce map (func :: args))
        | `func.{_ _ expr} (map expr)
        | `iterate.{_ inits expr} (reduce map (expr :: inits))
        | `continue.exprs (reduce map exprs)
        | `switch.{expr clauses}
            (reduce map (expr :: (LIST.map clauses (Func {_ body}. body))))
        | `cond.clauses
            (reduce map (LIST.concat_map clauses (Func {test body}. [test body])))
        | `if.{test then else} (reduce map [test then else])
        | `and.{test then} (reduce map [test then])
        | `or.{test else} (reduce map [test else])
        | `list.exprs (reduce map exprs)
        | `labeled.{_ expr} (map expr)
        | `match.{expr clauses}
            (reduce map (expr :: (LIST.map clauses (Func {_ body}. body))))
        ;
    In
    For (SET.list (f SET.empty))
        Let f (map expr)

Where

Let GRAPH Package "graph"
Let LIST Package "list"
Let OS Package "os"
Let SEARCH Package "search"
Let STRING Package "string"