El nuevo tutorial de SwiftUI tiene el siguiente código:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
La segunda línea de la palabra some
, y en su sitio se resalta como si fuera una palabra clave.
Swift 5.1 no parece tener some
una palabra clave, y no veo qué otra cosa some
podría estar haciendo la palabra allí, ya que va donde suele ir el tipo. ¿Existe una nueva versión no anunciada de Swift? ¿Es una función que se usa en un tipo de una manera que no conocía?
¿Qué hace la palabra clave some
?
Respuestas:
some View
es un tipo de resultado opaco introducido por SE-0244 y está disponible en Swift 5.1 con Xcode 11. Puede pensar en esto como un marcador de posición genérico "inverso".A diferencia de un marcador de posición genérico regular que satisface la persona que llama:
Un tipo de resultado opaco es un marcador de posición genérico implícito satisfecho por la implementación , por lo que puede pensar en esto:
como se ve así:
De hecho, el objetivo final con esta función es permitir genéricos inversos en esta forma más explícita, lo que también le permitiría agregar restricciones, por ejemplo
-> <T : Collection> T where T.Element == Int
. Vea esta publicación para más información .Lo principal que debemos sacar de esto es que una función que devuelve
some P
es aquella que devuelve un valor de un tipo concreto único específico que se ajusta aP
. Intentar devolver diferentes tipos conformes dentro de la función produce un error de compilación:Como el marcador de posición genérico implícito no puede ser satisfecho por múltiples tipos.
Esto contrasta con el retorno de una función
P
, que se puede usar para representar ambosS1
yS2
porque representa unP
valor conforme arbitrario :Bien, entonces, ¿qué beneficios tienen los tipos de resultados opacos
-> some P
sobre los tipos de retorno de protocolo-> P
?1. Los tipos de resultados opacos se pueden usar con PAT
Una limitación importante actual de los protocolos es que los PAT (protocolos con tipos asociados) no pueden usarse como tipos reales. Aunque esta es una restricción que probablemente se eliminará en una versión futura del lenguaje, debido a que los tipos de resultados opacos son efectivamente marcadores de posición genéricos, hoy en día se pueden usar con PAT.
Esto significa que puede hacer cosas como:
2. Los tipos de resultados opacos tienen identidad
Debido a que los tipos de resultados opacos obligan a que se devuelva un solo tipo concreto, el compilador sabe que dos llamadas a la misma función deben devolver dos valores del mismo tipo.
Esto significa que puede hacer cosas como:
Esto es legal porque el compilador sabe que ambos
x
yy
tienen el mismo tipo concreto. Este es un requisito importante para==
, donde ambos parámetros de tipoSelf
.Esto significa que espera dos valores que sean del mismo tipo que el tipo de conformación concreta. Incluso si
Equatable
fuera utilizable como tipo, no podría comparar dosEquatable
valores conformes arbitrarios entre sí, por ejemplo:Como el compilador no puede probar que dos
Equatable
valores arbitrarios tienen el mismo tipo concreto subyacente.De manera similar, si presentamos otra función de retorno de tipo opaco:
El ejemplo se vuelve ilegal porque aunque ambos
foo
ybar
devuelvensome Equatable
, sus marcadores de posición genéricos "inversos"Output1
yOutput2
podrían ser satisfechos por diferentes tipos.3. Los tipos de resultados opacos se componen con marcadores genéricos
A diferencia de los valores normales de tipo protocolo, los tipos de resultados opacos se componen bien con marcadores de posición genéricos regulares, por ejemplo:
Esto no habría funcionado si
makeP
hubiera regresadoP
, ya que dosP
valores pueden tener diferentes tipos de hormigón subyacentes, por ejemplo:¿Por qué usar un tipo de resultado opaco sobre el tipo concreto?
En este punto, puede estar pensando para usted mismo, ¿por qué no simplemente escribir el código como:
Bueno, el uso de un tipo de resultado opaco le permite convertir el tipo en
S
un detalle de implementación al exponer solo la interfaz proporcionada porP
, dándole flexibilidad para cambiar el tipo concreto más adelante en la línea sin romper ningún código que dependa de la función.Por ejemplo, podría reemplazar:
con:
sin romper ningún código que llame
makeP()
.Consulte la sección Tipos opacos de la guía de idiomas y la propuesta de evolución rápida para obtener más información sobre esta función.
fuente
return
no se requiere en las funciones de una sola expresiónfunc makeP() -> some P
yfunc makeP() -> P
? He leído la propuesta, y no puedo ver esta diferencia para sus muestras también.some P
sería necesarioLa otra respuesta hace un buen trabajo al explicar el aspecto técnico de la nueva
some
palabra clave, pero esta respuesta intentará explicar fácilmente por qué .Digamos que tengo un protocolo Animal y quiero comparar si dos animales son hermanos:
De esta manera, solo tiene sentido comparar si dos animales son hermanos si son del mismo tipo de animal.
Ahora déjenme crear un ejemplo de un animal solo como referencia
El camino sin
some T
Ahora digamos que tengo una función que devuelve un animal de una 'familia'.
Ahora surge el problema si trato de hacer esto:
Esto arrojará un error .
¿Por qué? Bueno, la razón es que cuando llamas a
animal1.isSibling(animal2)
Swift no sabes si los animales son perros, gatos o lo que sea. Hasta donde Swift sabe,animal1
yanimal2
podrían ser especies animales no relacionadas . Ya que no podemos comparar animales de diferentes tipos (ver arriba). Esto producirá un error¿Cómo
some T
resuelve este problema?Reescribamos la función anterior:
animal1
y no loanimal2
son , pero son una clase que implementa Animal .Animal
Lo que esto te permite hacer ahora es cuando llamas
animal1.isSibling(animal2)
, Swift lo sabeanimal1
yanimal2
son del mismo tipo.Entonces, la forma en que me gusta pensarlo:
(Descargo de responsabilidad de autopromoción) He escrito una publicación de blog que profundiza un poco más (el mismo ejemplo que aquí) sobre esta nueva función
fuente
some
en el tipo de retorno, funciona como una restricción al cuerpo de la función. Por lo tanto,some
requiere devolver solo un tipo concreto en todo el cuerpo de la función. Por ejemplo: si existereturn randomDog
, todas las demás devoluciones solo deben funcionar conDog
. Todos los beneficios provienen de esta restricción: disponibilidadanimal1.isSibling(animal2)
y beneficio de la compilación defunc animalFromAnimalFamily() -> some Animal
(porque ahoraSelf
se define bajo el capó). ¿Es correcto?La respuesta de Hamish es bastante impresionante y responde la pregunta desde una perspectiva técnica. Me gustaría agregar algunas ideas sobre por qué la palabra clave
some
se usa en este lugar en particular en los tutoriales SwiftUI de Apple y por qué es una buena práctica seguirla.some
No es un requisito!En primer lugar, no necesita declarar el
body
tipo de retorno del tipo como un tipo opaco. Siempre puede devolver el tipo concreto en lugar de usar elsome View
.Esto también se compilará. Cuando observa la
View
interfaz de 's, verá que el tipo de retorno debody
es un tipo asociado:Esto significa que usted especifica este tipo al anotar la
body
propiedad con un tipo particular de su elección. El único requisito es que este tipo necesita implementar elView
protocolo en sí.Ese puede ser un tipo específico que implementa
View
, por ejemploText
Image
Circle
o un tipo opaco que implementa
View
, es decirsome View
Vistas genéricas
El problema surge cuando intentamos usar una vista de pila como el
body
tipo de retorno del tipo, comoVStack
oHStack
:Esto no se compilará y obtendrá el error:
¡Esto se debe a que las vistas de pila en SwiftUI son tipos genéricos ! 💡 (Y lo mismo es cierto para las Listas y otros tipos de vista de contenedor).
Eso tiene mucho sentido porque puede conectar cualquier cantidad de vistas de cualquier tipo (siempre que se ajuste al
View
protocolo). El tipo concreto deVStack
en el cuerpo de arriba es en realidadCuando más tarde decidimos agregar una vista a la pila, su tipo concreto cambia. Si agregamos un segundo texto después del primero, obtenemos
Incluso si hacemos un cambio menor, algo tan sutil como agregar un espaciador entre el texto y la imagen, el tipo de pila cambia:
Por lo que puedo decir, esa es la razón por la cual Apple recomienda en sus tutoriales usar siempre
some View
, el tipo opaco más general que satisfacen todas las vistas, como elbody
tipo de retorno del. Puede cambiar la implementación / el diseño de su vista personalizada sin cambiar manualmente el tipo de retorno cada vez.Suplemento:
Si desea obtener una comprensión más intuitiva de los tipos de resultados opacos, recientemente publiqué un artículo que podría valer la pena leer:
🔗 ¿Qué es esto "algo" en SwiftUI?
fuente
Creo que todas las respuestas que faltan hasta ahora es que
some
es útil principalmente en algo como un DSL (lenguaje específico de dominio) como SwiftUI o una biblioteca / marco, que tendrá usuarios (otros programadores) diferentes de usted.Probablemente nunca usarías
some
en su código de aplicación normal, excepto tal vez en la medida en que pueda envolver un protocolo genérico para que pueda usarse como un tipo (en lugar de solo como una restricción de tipo). Lo quesome
hace es dejar que el compilador sepa qué tipo específico es algo, mientras coloca una fachada de supertipo frente a él.Por lo tanto, en SwiftUI, donde usted es el usuario, todos que necesita saber es que algo es así
some View
, mientras que detrás de escena puede continuar todo tipo de pañuelos de los que está protegido. Este objeto es, de hecho, un tipo muy específico, pero nunca necesitará saber de qué se trata. Sin embargo, a diferencia de un protocolo, es un tipo completo, porque donde quiera que aparezca es simplemente una fachada para algún tipo específico de pleno derecho.En una versión futura de SwiftUI, donde espera un
some View
, los desarrolladores podrían cambiar el tipo subyacente de ese objeto en particular. Pero eso no romperá su código, porque su código nunca mencionó el tipo subyacente en primer lugar.Por lo tanto,
some
en efecto, hace que un protocolo se parezca más a una superclase. Es casi un tipo de objeto real, aunque no del todo (por ejemplo, la declaración de método de un protocolo no puede devolver asome
).Entonces, si fuera a usar
some
para algo, lo más probable es que si que estuviera escribiendo un DSL o un marco / biblioteca para que otros lo usaran, y quisiera enmascarar los detalles de tipo subyacentes. Esto haría que su código sea más simple para que otros lo usen, y le permitiría cambiar los detalles de implementación sin romper su código.Sin embargo, también puede usarlo en su propio código como una forma de proteger una región de su código de los detalles de implementación enterrados en otra región de su código.
fuente
La
some
palabra clave de Swift 5.1 ( propuesta de evolución rápida ) se usa junto con un protocolo como tipo de retorno.Las notas de lanzamiento de Xcode 11 lo presentan así:
En el ejemplo anterior, no necesita decir que va a devolver un
Array
. Eso le permite incluso devolver un tipo genérico al que simplemente se ajustaCollection
.Tenga en cuenta también este posible error que puede enfrentar:
Significa que se supone que debes usar la disponibilidad para evitar
some
en iOS 12 y antes:fuente
some
en iOS 12 y versiones anteriores. Mientras lo hagas, deberías estar bien. El problema es solo que el compilador no te advierte que hagas esto.some
palabra clave en este ejemplo de código dado en Swift 5.0 o Swift 4.2. El error será: "La 'Colección' del protocolo solo puede usarse como una restricción genérica porque tiene requisitos de tipo Self o asociados "'algunos' significa tipo opaco. En SwiftUI, View se declara como un protocolo
Cuando crea su vista como Struct, se ajusta al protocolo View y le dice que el cuerpo var devolverá algo que confirmará al protocolo View. Es como una abstracción de protocolo genérica en la que no tiene que definir el tipo concreto.
fuente
Trataré de responder esto con un ejemplo práctico muy básico (¿de qué se trata este tipo de resultado opaco? )
Suponiendo que tiene un protocolo con un tipo asociado y dos estructuras que lo implementan:
Antes de Swift 5.1, a continuación es ilegal debido a un
ProtocolWithAssociatedType can only be used as a generic constraint
error:Pero en Swift 5.1 esto está bien (
some
agregado):Arriba está el uso práctico, ampliamente utilizado en SwiftUI para
some View
.Pero hay una limitación importante: el tipo de retorno debe conocerse en el momento de la compilación, por lo que a continuación nuevamente no funcionará dando
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
error:fuente
Un caso de uso simple que me viene a la mente es escribir funciones genéricas para tipos numéricos.
fuente
Para aquellos que estaban mareados por el tema, aquí un artículo muy descifrado y paso a paso gracias a Vadim Bulavin.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
fuente