Новый Учебник SwiftUI имеет следующий код:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
Во второй строке слово some, а у них на сайте выделено, как будто это ключевое слово.
Swift 5.1, похоже, не использует some в качестве ключевого слова, и я не вижу, что еще может делать слово some, поскольку оно идет туда, куда обычно идет тип. Есть ли новая необъявленная версия Swift? Это функция, которая используется для типа таким образом, о котором я не знал?
Что делает ключевое слово some?





some View — это непрозрачный тип результата, представленный SE-0244, и он доступен в Swift 5.1 с Xcode 11. Вы можете думать об этом как об «обратном» универсальном заполнителе.
В отличие от обычного универсального заполнителя, который удовлетворяет вызывающая сторона:
protocol P {}
struct S1 : P {}
struct S2 : P {}
func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.
Непрозрачный тип результата — это неявный общий заполнитель, удовлетворяющий реализация, поэтому вы можете думать об этом так:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
как это выглядит:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
На самом деле конечная цель этой функции — разрешить обратные дженерики в этой более явной форме, что также позволит вам добавлять ограничения, например -> <T : Collection> T where T.Element == Int. См. этот пост для получения дополнительной информации.
Главное, что следует вынести из этого, это то, что функция, возвращающая some P, — это та, которая возвращает значение определенного конкретного типа Один, соответствующего P. Попытка вернуть различные соответствующие типы внутри функции приводит к ошибке компилятора:
// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Поскольку неявный общий заполнитель не может быть удовлетворен несколькими типами.
Это отличается от функции, возвращающей P, которую можно использовать для представления обеS1 и S2, поскольку она представляет произвольное значение, соответствующее P:
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Итак, какие преимущества имеют непрозрачные типы результатов -> some P по сравнению с протокольными типами -> P?
Основным текущим ограничением протоколов является то, что PAT (протоколы со связанными типами) нельзя использовать в качестве фактических типов. Хотя это ограничение, вероятно, будет снято в будущей версии языка, поскольку непрозрачные типы результатов фактически являются просто общими заполнителями, сегодня их можно использовать с PAT.
Это означает, что вы можете делать такие вещи, как:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
Поскольку непрозрачные типы результатов требуют возврата одного конкретного типа, компилятор знает, что два вызова одной и той же функции должны возвращать два значения одного и того же типа.
Это означает, что вы можете делать такие вещи, как:
// foo() -> <Output : Equatable> Output {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.
Это допустимо, потому что компилятор знает, что и x, и y имеют один и тот же конкретный тип. Это важное требование для ==, где оба параметра типа Self.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Это означает, что он ожидает два значения того же типа, что и конкретный соответствующий тип. Даже если бы Equatable можно было использовать как тип, вы не смогли бы сравнить два произвольных Equatable соответствующих значения друг с другом, например:
func foo(_ x: Int) -> Equatable { // Assume this is legal.
if x > 10 {
return 0
} else {
return "hello world"
}
}
let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.
Поскольку компилятор не может доказать, что два произвольных значения Equatable имеют один и тот же базовый конкретный тип.
Аналогичным образом, если бы мы ввели другую функцию возврата непрозрачного типа:
// foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
// bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable {
return "" // The opaque result type is inferred to be String.
}
let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.
Пример становится недопустимым, потому что, хотя и foo, и bar возвращают some Equatable, их «обратные» общие заполнители Output1 и Output2 могут удовлетворяться разными типами.
В отличие от обычных значений протокола, непрозрачные типы результатов хорошо сочетаются с обычными общими заполнителями, например:
protocol P {
var i: Int { get }
}
struct S : P {
var i: Int
}
func makeP() -> some P { // Opaque result type inferred to be S.
return S(i: .random(in: 0 ..< 10))
}
func bar<T : P>(_ x: T, _ y: T) -> T {
return x.i < y.i ? x : y
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.
Это не сработало бы, если бы makeP только что вернул P, поскольку два значения P могут иметь разные базовые конкретные типы, например:
struct T : P {
var i: Int
}
func makeP() -> P {
if .random() { // 50:50 chance of picking each branch.
return S(i: 0)
} else {
return T(i: 1)
}
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.
В этот момент вы можете подумать про себя, почему бы просто не написать код так:
func makeP() -> S {
return S(i: 0)
}
Что ж, использование непрозрачного типа результата позволяет вам сделать тип S деталью реализации, раскрывая только интерфейс, предоставляемый P, что дает вам гибкость для изменения конкретного типа позже в строке без нарушения кода, который зависит от функции.
Например, вы можете заменить:
func makeP() -> some P {
return S(i: 0)
}
с участием:
func makeP() -> some P {
return T(i: 1)
}
без нарушения какого-либо кода, вызывающего makeP().
См. раздел Непрозрачные типы языкового справочника и предложение по эволюции Swift для получения дополнительной информации об этой функции.
@ielyamani Ага, хотя лично я предпочитаю оставить оператор return для функций. Возможно, это просто инерция, но что-то в этом мне кажется странным без утверждения return. Хотя мне нравится исключать return из вычисляемых свойств!
@RamasanPolat Почти то же самое в Python: python.org/dev/peps/pep-0484/#generics
Вкратце: возвращаемый тип func foo<T : Equatable>() -> T — это конкретный тип Equatable, выбранный вызывающей стороной, тогда как возвращаемый тип func foo() -> some Equatable — это конкретный тип Equatable, выбранный реализатором.
Я думаю, вопрос не в том, почему func makeP() -> some P лучше, чем func makeP() -> S, а в том, почему это лучше, чем func makeP() -> P. Вы привели пример, который показывает разницу. Но было бы неплохо сказать больше о положительных преимуществах здесь.
Я также хотел бы предварительно предположить, что имена, данные этой конструкции, к сожалению. Мне очень нравится ключевое слово some; что мне не нравится, так это описание этого как «непрозрачного» или «обратного общего». some P означает «конкретный P-адоптер». Таким образом, это способ вернуть тип адаптора протокола, как адаптор протокола. У меня нет подходящего названия для этого, но «непрозрачный» и «общий» его не охватывают (по моему нынешнему мнению).
Но в чем разница между: func makeP() -> some P и func makeP() -> P? Я прочитал предложение и не вижу этой разницы и для их образцов.
@matt, вам не нужно использовать generics в Python, это просто для IDE и линтеров, чтобы видеть ошибки во время компиляции. На самом деле вам даже не нужно предоставлять какую-либо информацию о типе в Python. Читабельность важнее, чем думает большинство людей.
@Артем, причин несколько. Для большой суммы -> some P будет действовать одинаково, однако, если у меня есть func f() -> some P и func g() -> some P, это не обязательно одно и то же. 1) по соображениям производительности Swift не обязательно будет выполнять преобразование и динамическую генерацию кода для типов 2) Swift знает, какой подкласс P они возвращают. Примером этого является то, что если у меня есть общий func f<T: P>(a: T, b: T), я могу вызвать его с экземплярами, возвращенными из f(), потому что Swift знает, что они одинаковы.
@matt Спасибо за отзыв! Я постараюсь отредактировать вещи, чтобы сделать различия между P и some P немного более явными. Что касается ментальной модели того, как думать о непрозрачных типах результатов, я все еще думаю, что «обратные дженерики» — лучший способ думать об этом, особенно учитывая, что конечная цель состоит в том, чтобы разрешить явные обратные дженерики, например -> <T : P> T с some P, является синтаксическим подслащенным эквивалентом.
Кроме того, some P, вероятно, в конечном итоге будет разрешено для параметров, где func foo(x: some P) будет синтаксическим сахаром для func foo<T : P>(x: T).
foo, и bar должны возвращать Int как some Equatable, чтобы продемонстрировать, что, хотя конкретные типы одинаковы, их нельзя сравнивать друг с другом.
Обработка типов Swifts - это беспорядок. Действительно ли эта специфика не может быть обработана во время компиляции? См. C# для справки, он обрабатывает все эти случаи неявно с помощью простого синтаксиса. Swift должен иметь бессмысленно явный синтаксис, почти культовый синтаксис, который действительно запутывает язык. Можете ли вы также объяснить обоснование дизайна для этого, пожалуйста? (Если у вас есть ссылка на предложение в github, это тоже было бы хорошо) Редактировать: только что заметил ссылку вверху.
Вы сказали: «предоставляет вам гибкость для изменения конкретного типа позже, не нарушая код, который зависит от функции», однако я не думаю, что это так. Если я правильно понимаю, компилятор выведет фактический тип, скажем, Apple. Если у вас есть какой-то другой код, который сравнивает такое выходное значение с другим Apple (предположим, что этот второй тип фиксирован), он будет правильно скомпилирован. Но если вы затем измените реализацию так, чтобы она возвращала апельсин, она сломается, потому что вы не сможете сравнить Apple с апельсином.
@Zmaster Компилятор будет рассматривать два непрозрачных возвращаемых типа как разные, даже если реализация для обоих возвращает один и тот же конкретный тип. Другими словами, конкретный выбранный конкретный тип скрыт от вызывающей стороны. (Я хотел расширить вторую половину своего ответа, чтобы сделать такие вещи немного более явными, но еще не дошел до этого).
Это действительно отличный ответ, который вносит невероятную ясность в some! Спасибо @Хэмиш!
значит ли это, что оно someнасколько похоже на ключевое слово C++ auto? Или это больше похоже на Java ? implements IMyInterface?
Я предполагаю, что результат лучше всего использовать с помехами. Или есть способ сказать, что конкретная переменная имеет тип some P? Например. var p: some P. Проблема, очевидно, заключается в том, чтобы хранить протокол с соответствующим типом.
Можно ли использовать body: <T: View> T вместо some View? или любой другой способ заменить some View?
Другой ответ хорошо объясняет технические аспекты нового ключевого слова some, но этот ответ попытается легко объяснить Зачем.
Допустим, у меня есть протокол Animal, и я хочу сравнить, являются ли два животных братьями и сестрами:
protocol Animal {
func isSibling(_ animal: Self) -> Bool
}
Таким образом, это имеет смысл сравнивать только двух животных, родных братьев и сестер, если они одного типа животного.
Теперь позвольте мне просто создать пример животного только для справки.
class Dog: Animal {
func isSibling(_ animal: Dog) -> Bool {
return true // doesn't really matter implementation of this
}
}
some TТеперь предположим, что у меня есть функция, которая возвращает животное из «семьи».
func animalFromAnimalFamily() -> Animal {
return myDog // myDog is just some random variable of type `Dog`
}
Note: this function won't actually compile. This because before the 'some' feature was added you cannot return a protocol type if the protocol uses 'Self' or generics. But let's say you can... pretending this upcasts myDog to abstract type Animal, let's see what happens
Теперь возникает проблема, если я попытаюсь сделать это:
let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()
animal1.isSibling(animal2) // error
Это вызовет ошибку.
Почему? Причина в том, что когда вы звоните animal1.isSibling(animal2), Свифт не знает, являются ли животные собаками, кошками или кем-то еще. Насколько известно Свифту, animal1 и animal2 могут быть неродственными видами животных.. Так как мы не можем сравнивать животных разных видов (см. выше). Это будет ошибкой
some T решает эту проблемуПерепишем предыдущую функцию:
func animalFromAnimalFamily() -> some Animal {
return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()
animal1.isSibling(animal2)
animal1 и animal2 это нетAnimal, ноэто класс, реализующий Animal.
Что это позволяет вам сделать сейчас, так это то, что когда вы вызываете animal1.isSibling(animal2), Swift знает, что animal1 и animal2 одного типа.
Итак, как мне нравится думать об этом:
some Tlets Swift know the what implementation ofTis being used but the user of the class doesn't.
(Отказ от ответственности за саморекламу) Я написал Сообщение блога, в котором более подробно рассматривается эта новая функция (тот же пример, что и здесь).
Итак, ваша идея состоит в том, что вызывающая сторона может воспользоваться тем фактом, что два вызова функции возвращают один и тот же тип, даже если вызывающая сторона не знает, какой это тип?
@matt по сути да. Та же концепция при использовании с полями и т. д. — вызывающему объекту дается гарантия того, что тип возвращаемого значения всегда будет одного и того же типа, но он не раскрывает точно, что это за тип.
@Downgoat Большое спасибо за прекрасный пост и ответ. Как я понял some в возвращаемом типе работает как ограничение на тело функции. Таким образом, some требует возврата только одного конкретного типа во всем теле функции. Например: если есть return randomDog, то все остальные возвраты должны работать только с Dog. Все преимущества проистекают из этого ограничения: доступность animal1.isSibling(animal2) и выгода от компиляции func animalFromAnimalFamily() -> some Animal (поскольку теперь Self определяется под капотом). Это правильно?
Эта строка была всем, что мне было нужно.
Ваш пример странный. Если метод «aminalFromAnimalFamiky» должен создавать животных из одного семейства, то почему в результате должно получиться более универсальное Животное?) Вы создали проблему и решили ее ))
Ключевое слово some из Swift 5.1 (предложение быстрой эволюции) используется в сочетании с протоколом в качестве возвращаемого типа.
Xcode 11 примечания к выпуску представляет это так:
Functions can now hide their concrete return type by declaring what protocols it conforms to, instead of specifying the exact return type:
func makeACollection() -> some Collection { return [1, 2, 3] }Code that calls the function can use the interface of the protocol, but doesn’t have visibility into the underlying type. (SE-0244, 40538331)
В приведенном выше примере вам не нужно говорить, что вы собираетесь вернуть Array. Это позволяет вам даже возвращать общий тип, который просто соответствует Collection.
Обратите также внимание на эту возможную ошибку, с которой вы можете столкнуться:
'some' return types are only available in iOS 13.0.0 or newer
Это означает, что вы должны использовать доступность, чтобы избежать some на iOS 12 и более ранних версиях:
@available(iOS 13.0, *)
func makeACollection() -> some Collection {
...
}
Большое спасибо за этот сфокусированный ответ и проблему с компилятором в бета-версии Xcode 11.
Вы должны использовать доступность, чтобы избежать some на iOS 12 и более ранних версиях. Пока вы это делаете, вы должны быть в порядке. Проблема только в том, что компилятор не предупреждает вас об этом.
Cœur, как вы заметили, краткое описание Apple объясняет все это: Теперь функции могут скрывать свой конкретный тип возвращаемого значения, объявляя, каким протоколам он соответствует, вместо указания точного типа возвращаемого значения. И тогда код, вызывающий функцию, может использовать интерфейс протокола. Аккуратно, а потом немного.
Это (скрытие конкретного возвращаемого типа) уже возможно без использования ключевого слова «некоторые». Это не объясняет эффект добавления «некоторых» в сигнатуру метода.
@VinceO'Sullivan Невозможно удалить ключевое слово some в данном примере кода в Swift 5.0 или Swift 4.2. Ошибка будет: "Протокол «Коллекция» может использоваться только в качестве общего ограничения, поскольку он имеет требования к Self или связанному типу."
ответ Хэмиша довольно крутой и отвечает на вопрос с технической точки зрения. Я хотел бы добавить некоторые мысли о том, почему ключевое слово some используется в этом конкретном месте в Руководства по SwiftUI Apple и почему это хорошая практика.
some Не является обязательным!Прежде всего, вы не используете нужно, чтобы объявить возвращаемый тип body непрозрачным. Вы всегда можете вернуть конкретный тип вместо использования some View.
struct ContentView: View {
var body: Text {
Text("Hello World")
}
}
Это также скомпилируется. Когда вы посмотрите на интерфейс View, вы увидите, что возвращаемый тип body является ассоциированным типом:
public protocol View : _View {
/// The type of view representing the body of this view.
///
/// When you create a custom view, Swift infers this type from your
/// implementation of the required `body` property.
associatedtype Body : View
/// Declares the content and behavior of this view.
var body: Self.Body { get }
}
Это означает, что ты указывает этот тип, аннотируя свойство body определенным типом по вашему выбору. Единственное требование состоит в том, что этот тип должен реализовать сам протокол View.
Это может быть тип специфический, реализующий View, например
TextImageCircleили тип непрозрачный, реализующий View, т.е.
some ViewПроблема возникает, когда мы пытаемся использовать представление стека в качестве возвращаемого типа body, например VStack или HStack:
struct ContentView: View {
var body: VStack {
VStack {
Text("Hello World")
Image(systemName: "video.fill")
}
}
}
Это не скомпилируется, и вы получите ошибку:
Reference to generic type 'VStack' requires arguments in <...>
Это потому, что представления стека в SwiftUI являются типами общий! ? (То же самое верно для Списки и других типов представления контейнера.)
Это имеет большой смысл, потому что вы можете подключить любое количество просмотров любого типа (если они соответствуют протоколу View). Конкретный тип VStack в теле выше на самом деле
VStack<TupleView<(Text, Image)>>
Когда позже мы решим добавить представление в стек, его конкретный тип изменится. Если мы добавим второй текст после первого, мы получим
VStack<TupleView<(Text, Text, Image)>>
Даже если мы внесем незначительное изменение, например, добавим разделитель между текстом и изображением, тип стека изменится:
VStack<TupleView<(Text, _ModifiedContent<Spacer, _FrameLayout>, Image)>>
Из того, что я могу сказать, это причина, по которой Apple рекомендует в своих руководствах всегда использовать some View, наиболее общий непрозрачный тип, который удовлетворяет всем представлениям, в качестве возвращаемого типа body. Вы можете изменить реализацию/макет вашего пользовательского представления, не изменяя каждый раз тип возвращаемого значения вручную.
Если вы хотите получить более интуитивное представление о непрозрачных типах результатов, я недавно опубликовал статью, которую, возможно, стоит прочитать:
Этот. Спасибо! Ответ Хэмиша был очень полным, но ваш ответ точно говорит мне, почему он используется в этих примерах.
Мне нравится идея «некоторых». Любая идея, влияет ли использование «некоторых» на время компиляции вообще?
@Mischa, так как сделать общие представления? с протоколом, который содержит представления и/или другое поведение?
Я думаю, что все ответы до сих пор отсутствуют, так это то, что some полезен в первую очередь в чем-то вроде DSL (предметно-ориентированного языка), такого как SwiftUI или библиотека/фреймворк, который будет иметь пользователи (другие программисты), отличные от вас.
Вы, вероятно, никогда не будете использовать some в своем обычном коде приложения, за исключением, возможно, того, что он может обернуть общий протокол, чтобы его можно было использовать как тип (а не просто как ограничение типа). Что делает some, так это позволяет компилятору хранить информацию о том, какой конкретный тип что-то представляет, при этом помещая перед ним фасад супертипа.
Таким образом, в SwiftUI, где вы являетесь пользователем, все, что ты нужно знать, это то, что что-то является some View, в то время как за кулисами могут происходить всевозможные пустяки, от которых вы защищены. Этот объект на самом деле является очень специфическим типом, но вам никогда не придется слышать о том, что это такое. Тем не менее, в отличие от протокола, это полноценный тип, потому что везде, где он появляется, это просто фасад для какого-то конкретного полноценного типа.
В будущей версии SwiftUI, где вы ожидаете some View, разработчики могут изменить базовый тип этого конкретного объекта. Но это не сломает ваш код, потому что в нем никогда не упоминался базовый тип.
Таким образом, some фактически делает протокол более похожим на суперкласс. Это почти реальный тип объекта, хотя и не совсем (например, объявление метода протокола не может возвращать some).
Поэтому, если вы собираетесь использовать some для чего-либо, это, скорее всего, будет, если ты пишет DSL или фреймворк/библиотеку для использования другими, и вы хотите замаскировать детали базового типа. Это сделает ваш код более простым для использования другими и позволит вам изменить детали реализации, не нарушая их код.
Однако вы также можете использовать его в своем собственном коде как способ защиты одной области вашего кода от деталей реализации, скрытых в другой области вашего кода.
Я чувствую, что этот ответ (и ваш комментарий в ответе Downgoat) является настоящим ответом. Короткая версия - «некоторые» просто означают, что данная функция всегда возвращает один конкретный конкретный тип (который вас не волнует, но соответствует протоколу, который вы делаете). Примеры в других ответах оказывают медвежью услугу, пример родственного брата работает только тогда, когда сравниваемое «какое-то животное» получено из того же метода для его создания.
«некоторые» означает непрозрачный тип. В SwiftUI View объявлен как протокол
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {
/// The type of view representing the body of this view.
///
/// When you create a custom view, Swift infers this type from your
/// implementation of the required `body` property.
associatedtype Body : View
/// Declares the content and behavior of this view.
var body: Self.Body { get }
}
Когда вы создаете свое представление как Struct, вы соответствуете протоколу представления и сообщаете, что тело переменной вернет что-то, что будет подтверждать протокол представления. Это похоже на общую абстракцию протокола, где вам не нужно определять конкретный тип.
Я попытаюсь ответить на это очень простым практическим примером (о чем этот непрозрачный тип результата)
Предполагая, что у вас есть протокол со связанным типом и две структуры, реализующие его:
protocol ProtocolWithAssociatedType {
associatedtype SomeType
}
struct First: ProtocolWithAssociatedType {
typealias SomeType = Int
}
struct Second: ProtocolWithAssociatedType {
typealias SomeType = String
}
До Swift 5.1 приведенное ниже недопустимо из-за ошибки ProtocolWithAssociatedType can only be used as a generic constraint:
func create() -> ProtocolWithAssociatedType {
return First()
}
Но в Swift 5.1 это нормально (добавлено some):
func create() -> some ProtocolWithAssociatedType {
return First()
}
Выше приведено практическое использование, широко используемое в SwiftUI для some View.
Но есть важное ограничение один - возвращаемый тип должен быть известен во время компиляции, поэтому ниже снова не будет работать, выдавая ошибку Function declares an opaque return type, but the return statements in its body do not have matching underlying types:
func create() -> some ProtocolWithAssociatedType {
if (1...2).randomElement() == 1 {
return First()
} else {
return Second()
}
}
Для тех, у кого закружилась тема, вот очень расшифровка и пошаговая статья спасибо Вадиму Булавину.
https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/
упростить, если вы знаете разницу между
var x = 5
против
int x =5
Тогда ты узнаешь some.
Компилятор это знает, и вы это знаете. Минимальные усилия для того, чтобы сказать, что вы соответствуете чему-то, не указывая специфики (общие типы, которые он использует)
Простой способ понять, как kindOf в Objc
В моем понимании (может неправильно)
Звонок, который у меня был
Protocol View{}
class Button: View { // subclass of View }
//this class not a subclass of View
class ButtonBuilder<T> where T:View { //using T as View here }
потом
var body: View = Button() // ok
var body: View = ButtonBilder() //not ok
var body: some View = ButtonBilder() //ok
Так
some Protocol
Может обрабатывать общий класс, использующий этот протокол, как общий в своем собственном коде как подкласс протокола.
Вы можете предположить, как универсальный в быстром.
В сообщении Миши выше (извините, я пока не могу напрямую добавить комментарий) говорится, что some является необязательным, если вы не используете общие типы, такие как VStack и т. д. И это потому, что some является наиболее общим непрозрачным типом, который удовлетворяет все представления. Таким образом, его использование здесь помогает устранить ошибку компиляции.
Кажется, что some очень близок к тому, что делает метод Combine eraseToAnyPublisher().
Для тех, у кого закружилась тема, вот очень расшифровка и пошаговая статья спасибо Вадиму Булавину. vadimbulavin.com/…