Yog.FSharp
Getting Started Examples API Reference GitHub

Deterministic Task Ordering

This example demonstrates lexicographical topological sorting, which provides a deterministic and alphabetically sorted order for tasks with dependencies.

Problem

In a build system or project plan, many topological orders may be valid. Sometimes we want the order to be stable and follow alphabetical naming conventions.

#I "../../src/Yog.FSharp/bin/Debug/net10.0"
#r "Yog.FSharp.dll"

open Yog.Model
open Yog.Traversal

// Model task dependencies (Pre-requisite, Step)
let dependencies = [
    ("C", "A"); ("C", "F"); ("A", "B"); ("A", "D"); ("B", "E"); ("D", "E"); ("F", "E")
]

let graph: Graph<string, unit> =
    dependencies
    |> List.fold (fun (g: Graph<string, unit>) (prereq, step) ->
        let pid = int (prereq.[0])
        let sid = int (step.[0])
        g
        |> addNode pid prereq
        |> addNode sid step
        |> addEdge pid sid ()
    ) (empty Directed)

printfn "=== Lexicographical Topological Sort ==="

// Sort alphabetically by node data (task names)
match lexicographicalTopologicalSort compare graph with
| Ok order ->
    let taskNames =
        order
        |> List.map (fun id -> graph.Nodes.[id])
        |> String.concat ""
    
    printfn "Task execution order: %s" taskNames
    printfn "(Expected: CABDFE)"
| Error () ->
    printfn "Circular dependency detected!"

Output

=== Lexicographical Topological Sort ===
Task execution order: CABDFE
(Expected: CABDFE)
val dependencies: (string * string) list
val graph: obj
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
type unit = Unit
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T with get member IsEmpty: bool with get member Item: index: int -> 'T with get ...
val fold<'T,'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State
val g: obj
val prereq: string
val step: string
val pid: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val sid: int
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val compare: e1: 'T -> e2: 'T -> int (requires comparison)
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val order: obj list
val taskNames: string
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val id: obj
module String from Microsoft.FSharp.Core
val concat: sep: string -> strings: string seq -> string
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>