Alternate ways to create combinations of a range of numbers

Algorithms product is designed for heterogeneous argument types. as you said it "is lacking for any deeper nesting than two for loops". With each nesting, the result tuple get deeper and it's kind of confusing to destructure. I'm amazed at you generic code. I'm not able write code like this.

For my own need, I wrote a product function for homogeneous argument type that can take an array and computet the product result using Algorithms' product:

public func product<C: Collection>(_ terms: Array<C>) -> Array<Array<C.Element>> {
    switch terms.count {
    case 0:
        return []
    case 1:
        return terms[0].map { [$0] }
    default:
        var result = product(terms[0], terms[1]).map { [$0, $1] }
        for term in terms.dropFirst(2) {
            result = product(result, term).map { $0.0 + [$0.1] }
        }
        return result
    }
}

with this, OP's problem can be solved:

product(Array(repeating: 0...2, count: 4))

I thought support for homogeneous product argument type is a very common need so it should be built-in to Algorithms: Algorithms package: product(_:_) limited to two product terms only, for homogeneous element type, there should be one for any number of product terms?