Estendendo a coleção com uma propriedade / método recursivo que depende do tipo de elemento

Dentro do contexto deessa questão, Pensei em como implementar uma propriedade ou método que contatudo níveis de aninhamento em coleções.

Intuitivamente, algo que deve 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 }
        }
    }
}

No entanto, não temos permissão para converter valores em tipos de protocolo que possuem tipos associados.

Então, pensei em tornar o tipo de elemento mais explícito, assim:

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
    }
}

Mas isso aparentemente está pedindo muito do tipo inferrador.

Agora dei um passo atrás e decidi parar de tentar colocar tudo em uma extensão:

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 }
    }
}

Agora issocompila -- yay! - mas a expedição está desativada:$1.flatCount não se liga à segunda versão recursiva, mas sempre à primeira versão simples. Isso é,flatCount conta apenas o primeiro nível de aninhamento.

Existe uma maneira de manipular tipos e / ou despachar de maneira a expressar essa função? Ou vou fazer isso de uma maneira completamente indireta (ou duas)?

Nota lateral: no último exemplo e na primeira função, eu não uso

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

porque isso não compila; aqui,$0 é opar de ambos os parâmetros anônimos! Eu acho que esse é um comportamento desnecessariamente surpreendente e publicadouma solicitação de mudança para o bugtracker Swift.

questionAnswers(2)

yourAnswerToTheQuestion