Swift 4.1: por que a Apple renomeou flatMap para compactMap

Olá Habr!

Meu nome é Alexander Zimin, sou desenvolvedor iOS no Badoo. Esta é uma tradução de um artigo do meu colega Schwib, no qual ele descreveu como era a função flatMap no Swift e por que uma de suas sobrecargas foi renomeada para compactMap. O artigo é útil para entender os processos que ocorrem no repositório Swift e sua evolução , bem como para o desenvolvimento geral.



Na programação funcional, há uma definição clara de qual flatMap função flatMap . O método flatMap pega uma lista e uma função de transformação (que para cada transformação espera obter zero ou mais valores), aplica-a a cada elemento da lista e cria uma lista única (nivelada). Esse comportamento é diferente da função de map simples, que aplica uma transformação a cada valor e espera obter apenas um valor para cada transformação.



Para várias versões, o Swift possui um map e flatMap . No entanto, no Swift 4.1, não é mais possível aplicar o flatMap a uma sequência de valores e ainda passar um fechamento que retorna um valor opcional. Agora existe um método compactMap para compactMap .

No início, pode não ser tão fácil entender a essência da inovação. Se o flatMap funcionou bem, por que introduzir um método separado? Vamos descobrir.

A biblioteca padrão Swift anterior à versão 4.1 forneceu três implementações de sobrecargas para o flatMap :

 1. Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element],  S : Sequence 2. Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? 3. Sequence.flatMap<U>(_: (Element) -> U?) -> [U] 

Vamos analisar as três opções e ver o que elas fazem.

Sequence.flatMap <S> (_: (Elemento) -> S) -> [S.Element], em que S: Sequence


A primeira sobrecarga é para sequências nas quais um fechamento pega um elemento dessa sequência e converte em outra sequência.
flatMap todas essas seqüências transformadas na sequência final retornada como resultado. Por exemplo:

 let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7, 8, 9] 

Este é um ótimo exemplo de como o método flatMap deve funcionar. Vamos transformar (mapear) cada elemento da lista de fontes e criar uma nova sequência. Graças ao flatMap resultado final é uma estrutura nivelada de sequências transformadas.

Optional.flatMap <U> (_: (Embrulhado) -> U?) -> U?


A segunda sobrecarga é para tipos opcionais. Se o tipo opcional que você está chamando tiver um valor, o fechamento será chamado com o valor sem o invólucro opcional (valor não empacotado) e você poderá retornar o valor opcional convertido.

 let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 } // 4 let b: Int? = nil let transformedB = b.flatMap { $0 * 2 } // nil 

Sequence.flatMap <U> (_: (Elemento) -> U?) -> [U]


A terceira sobrecarga ajudará você a entender para que compactMap o compactMap . Esta versão parece a mesma que a primeira, mas há uma diferença importante. Nesse caso, o fechamento retorna opcional. flatMap processa, ignorando os valores nulos retornados e inclui todo o restante - no resultado como valores sem um invólucro.

 let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7] 

Mas, neste caso, a encomenda não é realizada. Portanto, esta versão do flatMap mais próxima do map que a definição puramente funcional do flatMap . E o problema com essa sobrecarga é que você não pode usá-lo corretamente onde o map funcionaria perfeitamente.

 let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 } // same as array.map { $0 } 

Esse uso do flatMap corresponde à terceira sobrecarga, envolvendo implicitamente o valor convertido em opcional e removendo o wrapper para adicionar ao resultado. A situação se torna especialmente interessante se as conversões de string não forem usadas corretamente.

 struct Person { let name: String } let people = [Person(name: “Foo”), Person(name: “Bar”)] let names = array.flatMap { $0.name } 

No Swift anterior à versão 4.0, obteríamos uma conversão para [“Foo”, “Bar”] . Mas, começando na versão 4.0, os valores das cadeias implementam o protocolo Collection. Portanto, nosso uso do flatMap nesse caso, em vez da terceira sobrecarga, corresponderá ao primeiro e obteremos um resultado "nivelado" dos valores convertidos: [“F”, “o”, “o”, “B”, “a”, “r”]

Ao chamar flatMap você não receberá um erro, porque isso é permitido. Mas a lógica será quebrada, porque o resultado é do tipo Array<Character>.Type , não a Array<String>.Type esperada Array<String>.Type .

Conclusão


Para evitar o uso indevido do flatMap , a terceira versão sobrecarregada foi removida da nova versão Swift. E para resolver o mesmo problema (removendo valores nulos) agora você precisa usar um método separado - compactMap .

Source: https://habr.com/ru/post/pt414809/


All Articles