Colección extensible con una propiedad / método recursivo que depende del tipo de elemento

En el contexto deesta pregunta, Pensé en cómo se podría implementar una propiedad o método que cuentetodas Niveles de anidamiento en colecciones.

Intuitivamente, algo que debería funcionar:

extension Collection { 
    var flatCount: Int {
        if self.count == 0 {
            return 0
        } else if self.first is Collection { // .Iterator.Element: Collection
            return self.reduce(0) { (res, elem) -> Int in
                res + (elem as! Collection).flatCount // ERROR
            }
        } else {
            return self.reduce(0) { (res,_) in res + 1 }
        }
    }
}

Sin embargo, no podemos emitir valores a tipos de protocolo que tengan tipos asociados.

Entonces pensé hacer que el tipo de elemento sea más explícito, así:

extension Collection {
    var flatCount: Int {
        return Self.flatCountH(self)
    }

    private static final func 
        flatCountH<C: Collection, D>(_ c: C) -> Int 
            where Iterator.Element == D, D: Collection {
        return c.reduce(0) { (res: Int, elem: D) -> Int in 
            (res + elem.flatCount) as Int // Ambiguous type 
        }
    }

    private static final func flatCountH<C: Collection>(_ c: C) -> Int {
        return c.reduce(0) { $0 + $1.flatCount } // Unable to infer closure type
    }
}

Pero esto aparentemente está pidiendo demasiado del tipo inferidor.

Ahora di un paso atrás y decidí dejar de tratar de combinar todo en una extensión:

extension Collection {
    var flatCount: Int {
        // There's no count on Collection, so...
        return self.reduce(0) { (res,_) in res + 1 }
    }
}

extension Collection where Iterator.Element: Collection {
    var flatCount: Int {
        return self.reduce(0) { $0 + $1.flatCount }
    }
}

Ahora estocompila -- ¡Hurra! - pero el envío está apagado:$1.flatCount no se une a la segunda versión recursiva, sino siempre a la primera versión simple. Es decir,flatCount solo cuenta el primer nivel de anidamiento.

¿Hay alguna manera de hacer malabarismos con los tipos y / o el envío de una manera de expresar esta función? ¿O lo estoy haciendo de una manera completamente indirecta (o dos)?

Nota al margen: en el último ejemplo y en la primera función, no uso

self.reduce(0) { $0 + 1 }

porque eso no compila; aquí,$0 es elpar de ambos parámetros anónimos! Creo que este es un comportamiento innecesariamente sorprendente y publicadouna solicitud de cambio al rastreador de errores Swift.

Respuestas a la pregunta(2)

Su respuesta a la pregunta