Sorting a list into different bags according to predicate
Here's another (Subjectively better or worse) way to write this, however it does require comparison unlike yours:

let sortIntoBags&lt;'T,'TKey when 'TKey : comparison &gt; (predicate:'TKey*'T -&gt; bool) (bagKeys:'TKey list) (items:'T list) : ('TKey*'T list) list * 'T list =
    let bagMap:Map&lt;'TKey,'T list&gt; = bagKeys |&gt; Seq.map(fun k -&gt; k,List.empty) |&gt; Map.ofSeq
    items
    |&gt; List.rev
    |&gt; List.fold(fun (bagMap,unmatched:'T list) (item:'T) -&gt;
        match bagMap |&gt; Map.tryFindKey(fun k _ -&gt; predicate(k,item)) with
        | Some k -&gt; bagMap |&gt; Map.add k (item::bagMap.[k]), unmatched
        | None -&gt; bagMap,item::unmatched

    ) (bagMap,List.empty)
    |&gt; fun (m,x)-&gt;
        m |&gt; Map.toList, x


and the xunit tests I used to make sure it matched the output

open global.Xunit

[&lt;Fact&gt;]
let originalQuestion2 () =
   let l= [1..25]
   let bags= [2; 3; 5; 7]
   let isDivisorFor (x, y) = 0=y%x
   let expected = [(2, [2; 4; 6; 8; 10; 12; 14; 16; 18; 20; 22; 24]); (3, [3; 9; 15; 21]);(5, [5; 25]); (7, [7])], [1; 11; 13; 17; 19; 23]
   let actual = l |&gt; BagSort.sortIntoBags isDivisorFor bags
   printfn "%A" actual
   Console.Error.WriteLine(sprintf "%A" actual)
   Trace.WriteLine(sprintf "%A" actual)
   Assert.Equal(expected, actual)


[&lt;Fact&gt;]
let originalQuestionUnfolded2 () =
   let l= [1..25]
   let bags= [2; 3; 5; 7]
   let isDivisorFor (x, y) = 0=y%x
   let expected =
        let a =
            [   2, [2; 4; 6; 8; 10; 12; 14; 16; 18; 20; 22; 24]
                3, [3; 9; 15; 21]
                5, [5; 25]
                7, [7]
            ]
        let b = [1; 11; 13; 17; 19; 23]
        a,b
   let actual = l |&gt; BagSort.sortIntoBags isDivisorFor bags
   Assert.Equal(expected, actual)
   Assert.Equal((fst expected |&gt; fun x -&gt; x.[0]), fst actual |&gt; fun x -&gt; x.[0])