O novo tutorial do SwiftUI possui o seguinte código:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
A segunda linha da palavra some
e em seu site é destacada como se fosse uma palavra-chave.
O Swift 5.1 parece não ter some
uma palavra-chave, e não vejo o que mais a palavra some
poderia estar fazendo lá, uma vez que vai para onde o tipo costuma ir. Existe uma nova versão não anunciada do Swift? É uma função que está sendo usada em um tipo de uma maneira que eu não conhecia?
O que a palavra-chave some
faz?
Respostas:
some View
é um tipo de resultado opaco, conforme introduzido pela SE-0244 e está disponível no Swift 5.1 com Xcode 11. Você pode pensar nisso como um espaço reservado genérico "reverso".Ao contrário de um espaço reservado genérico regular que é atendido pelo chamador:
Um tipo de resultado opaco é um espaço reservado genérico implícito satisfeito com a implementação , para que você possa pensar sobre isso:
como esta:
De fato, o objetivo final com esse recurso é permitir genéricos reversos dessa forma mais explícita, o que também permitiria adicionar restrições, por exemplo
-> <T : Collection> T where T.Element == Int
. Veja este post para mais informações .A principal coisa a tirar de tudo isto é que uma função que retorna
some P
é aquele que retorna um valor de um específico único tipo concreto que está em conformidade comP
. Tentar retornar diferentes tipos conformes dentro da função gera um erro do compilador:Como o espaço reservado genérico implícito não pode ser satisfeito por vários tipos.
Isso contrasta com uma função retornada
P
, que pode ser usada para representar ambosS1
eS2
porque representa umP
valor conforme arbitrário :Certo, quais benefícios os tipos de resultados opacos
-> some P
têm sobre os tipos de retorno de protocolo-> P
?1. Tipos de resultado opacos podem ser usados com PATs
Uma grande limitação atual dos protocolos é que os PATs (protocolos com tipos associados) não podem ser usados como tipos reais. Embora seja uma restrição que provavelmente será levantada em uma versão futura do idioma, porque os tipos de resultados opacos são efetivamente apenas espaços reservados genéricos, eles podem ser usados com PATs hoje.
Isso significa que você pode fazer coisas como:
2. Tipos de resultado opacos têm identidade
Como os tipos de resultados opacos impõem que um único tipo concreto seja retornado, o compilador sabe que duas chamadas para a mesma função devem retornar dois valores do mesmo tipo.
Isso significa que você pode fazer coisas como:
Isso é legal porque o compilador conhece os dois
x
ey
tem o mesmo tipo concreto. Este é um requisito importante para==
, onde ambos os parâmetros do tipoSelf
.Isso significa que ele espera dois valores que são do mesmo tipo que o tipo de conformidade concreto. Mesmo se
Equatable
fosse utilizável como um tipo, você não seria capaz de comparar doisEquatable
valores de conformidade arbitrários entre si, por exemplo:Como o compilador não pode provar que dois
Equatable
valores arbitrários têm o mesmo tipo concreto subjacente.De maneira semelhante, se introduzirmos outra função de retorno de tipo opaco:
O exemplo se torna ilegal porque, embora ambos
foo
ebar
retornemsome Equatable
, seus espaços reservados genéricos "reversos"Output1
eOutput2
podem ser satisfeitos por diferentes tipos.3. Tipos de resultado opacos são compostos por espaços reservados genéricos
Ao contrário dos valores regulares do tipo de protocolo, os tipos de resultados opacos são bem compostos com espaços reservados genéricos regulares, por exemplo:
Isso não teria funcionado se
makeP
tivesse acabado de retornarP
, pois doisP
valores podem ter tipos concretos subjacentes diferentes, por exemplo:Por que usar um tipo de resultado opaco sobre o tipo de concreto?
Nesse ponto, você pode estar pensando por que não escrever o código como:
Bem, o uso de um tipo de resultado opaco permite tornar o tipo
S
um detalhe de implementação, expondo apenas a interface fornecidaP
, oferecendo flexibilidade de alterar o tipo de concreto posteriormente na linha sem quebrar nenhum código que dependa da função.Por exemplo, você pode substituir:
com:
sem quebrar nenhum código que chama
makeP()
.Consulte a seção Tipos opacos do guia de idiomas e a proposta de evolução Swift para obter mais informações sobre esse recurso.
fonte
return
não é necessário em funções de expressão únicafunc makeP() -> some P
efunc makeP() -> P
? Eu li a proposta e não vejo essa diferença também nas amostras deles.some P
seria necessárioA outra resposta explica bem o aspecto técnico da nova
some
palavra-chave, mas essa resposta tentará explicar facilmente o porquê .Digamos que tenho um protocolo Animal e quero comparar se dois animais são irmãos:
Dessa forma, só faz sentido comparar se dois animais são irmãos se eles são do mesmo tipo de animal.
Agora, deixe-me criar um exemplo de animal apenas para referência
O caminho sem
some T
Agora, digamos que eu tenho uma função que retorna um animal de uma 'família'.
Agora, a questão é: se eu tentar fazer isso:
Isso gerará um erro .
Por quê? Bem, o motivo é que, quando você liga para
animal1.isSibling(animal2)
Swift, não sabe se os animais são cães, gatos ou o que seja. Tanto quanto Swift sabe,animal1
eanimal2
pode ser uma espécie animal não relacionada . Como não podemos comparar animais de diferentes tipos (veja acima). Isto irá erroComo
some T
resolve esse problemaVamos reescrever a função anterior:
animal1
e nãoanimal2
são , mas são classes que implementam Animal .Animal
O que isso permite que você faça agora é quando você liga
animal1.isSibling(animal2)
, Swift sabe dissoanimal1
eanimal2
é do mesmo tipo.Então, da maneira que eu gosto de pensar sobre isso:
(Isenção de responsabilidade de autopromoção) Escrevi uma postagem de blog que se aprofundou um pouco mais (o mesmo exemplo aqui) sobre esse novo recurso
fonte
some
no tipo de retorno, funciona como restrição ao corpo da função. Portanto, ésome
necessário retornar apenas um tipo concreto em todo o corpo da função. Por exemplo: se houverreturn randomDog
, todos os outros retornos devem funcionar apenas comDog
. Todos os benefícios advêm dessa restrição: disponibilidadeanimal1.isSibling(animal2)
e benefício da compilação defunc animalFromAnimalFamily() -> some Animal
(porque agoraSelf
é definido sob o capô). Está correto?A resposta de Hamish é bastante impressionante e responde à pergunta de uma perspectiva técnica. Gostaria de acrescentar algumas reflexões sobre por que a palavra
some
- chave é usada neste local específico nos tutoriais SwiftUI da Apple e por que é uma boa prática a seguir.some
não é um requisito!Primeiro de tudo, você não precisa declarar o
body
tipo de retorno do tipo como opaco. Você sempre pode retornar o tipo concreto em vez de usar osome View
.Isso também será compilado. Quando você olha para a
View
interface da, verá que o tipo de retornobody
é um tipo associado:Isso significa que você especifica esse tipo anotando a
body
propriedade com um tipo específico de sua escolha. O único requisito é que esse tipo precise implementar oView
próprio protocolo.Esse pode ser um tipo específico que implementa
View
, por exemploText
Image
Circle
ou um tipo opaco que implementa
View
, ou seja,some View
Visualizações genéricas
O problema surge quando tentamos usar uma exibição de pilha como o
body
tipo de retorno, comoVStack
ouHStack
:Isso não será compilado e você receberá o erro:
Isso ocorre porque as visualizações de pilha no SwiftUI são tipos genéricos ! 💡 (E o mesmo se aplica a Listas e outros tipos de exibição de contêiner.)
Isso faz muito sentido, porque você pode conectar qualquer número de visualizações de qualquer tipo (desde que esteja em conformidade com o
View
protocolo). O tipo de concretoVStack
no corpo acima é realmenteQuando mais tarde decidimos adicionar uma exibição à pilha, seu tipo concreto é alterado. Se adicionarmos um segundo texto após o primeiro, obtemos
Mesmo se fizermos uma pequena alteração, algo tão sutil quanto adicionar um espaçador entre o texto e a imagem, o tipo da pilha muda:
Pelo que sei, essa é a razão pela qual a Apple recomenda em seus tutoriais sempre usar
some View
, o tipo opaco mais geral que todas as visualizações satisfazem, como obody
tipo de retorno. Você pode alterar a implementação / o layout da sua visualização personalizada sem alterar manualmente o tipo de retorno toda vez.Suplemento:
Se você deseja obter uma compreensão mais intuitiva dos tipos de resultados opacos, publiquei recentemente um artigo que vale a pena ler:
🔗 O que é isso "alguns" no SwiftUI?
fonte
Acho que todas as respostas até agora estão faltando e que
some
são úteis principalmente em algo como uma DSL (linguagem específica de domínio), como SwiftUI ou uma biblioteca / estrutura, que terá usuários (outros programadores) diferentes de você.Você provavelmente nunca usaria
some
em seu código de aplicativo normal, exceto, talvez, na medida em que ele pode quebrar um protocolo genérico para que possa ser usado como um tipo (em vez de apenas como uma restrição de tipo). O quesome
faz é permitir que o compilador mantenha um conhecimento do tipo específico de algo, enquanto coloca uma fachada de supertipo na frente dele.Assim, no SwiftUI, onde você é o usuário, tudo o que você precisa saber é que algo é um
some View
, enquanto nos bastidores todo tipo de bobagem pode acontecer a partir do qual você está protegido. Na verdade, esse objeto é um tipo muito específico, mas você nunca precisará ouvir sobre o que é. No entanto, diferentemente de um protocolo, é um tipo de pleno direito, porque, onde quer que apareça, é apenas uma fachada para algum tipo específico de pleno direito.Em uma versão futura do SwiftUI, na qual você espera um
some View
, os desenvolvedores podem alterar o tipo subjacente desse objeto em particular. Mas isso não quebrará seu código, porque ele nunca mencionou o tipo subjacente em primeiro lugar.Portanto,
some
com efeito, torna um protocolo mais parecido com uma superclasse. É quase um tipo de objeto real, embora não exatamente (por exemplo, a declaração de método de um protocolo não pode retornar asome
).Então, se você ia usar
some
para qualquer coisa, ele provavelmente seria se você estivesse escrevendo um DSL ou framework / biblioteca para uso por outros, e você queria para mascarar detalhes tipo subjacente. Isso tornaria seu código mais simples para o uso de outras pessoas e permitiria que você alterasse os detalhes da implementação sem quebrar o código.No entanto, você também pode usá-lo em seu próprio código como uma maneira de proteger uma região do seu código dos detalhes da implementação ocultos em outra região do seu código.
fonte
A
some
palavra-chave do Swift 5.1 ( proposta de evolução rápida ) é usada em conjunto com um protocolo como um tipo de retorno.As notas de versão do Xcode 11 apresentam assim:
No exemplo acima, você não precisa dizer que retornará um
Array
. Isso permite que você retorne um tipo genérico que apenas esteja em conformidadeCollection
.Observe também este possível erro que você pode enfrentar:
Isso significa que você deve usar a disponibilidade para evitar
some
no iOS 12 e antes:fonte
some
no iOS 12 e anteriores. Contanto que você faça, você deve ficar bem. O problema é apenas que o compilador não avisa para fazer isso.some
palavra - chave neste exemplo de código no Swift 5.0 ou no Swift 4.2. O erro será: "O protocolo 'Coleção' só pode ser usado como uma restrição genérica porque possui requisitos de tipo Próprio ou associado "'some' significa tipo opaco. No SwiftUI, o View é declarado como um protocolo
Ao criar sua visualização como Struct, você está em conformidade com o protocolo View e informa que o corpo var retornará algo que estará confirmando o View Protocol. É como uma abstração genérica de protocolo, na qual você não precisa definir o tipo concreto.
fonte
Vou tentar responder com um exemplo prático muito básico (sobre o que é esse tipo de resultado opaco )
Supondo que você tenha um protocolo com o tipo associado e duas estruturas implementando-o:
Antes do Swift 5.1, abaixo é ilegal devido a
ProtocolWithAssociatedType can only be used as a generic constraint
erro:Mas no Swift 5.1, isso é bom (
some
adicionado):Acima está o uso prático, amplamente utilizado no SwiftUI para
some View
.Mas há uma limitação importante: o tipo de retorno precisa ser conhecido no momento da compilação; portanto, abaixo novamente, não funcionará dando
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
erro:fonte
Um caso de uso simples que vem à mente é escrever funções genéricas para tipos numéricos.
fonte
Para quem ficou tonto com o assunto, aqui está um artigo muito decodificador e passo a passo, graças a Vadim Bulavin.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
fonte