Допустимо ли объявить условное соответствие для existential-типа any P в зависимости от скрытого конкретного типа?
Нет. В Swift нельзя объявить условное соответствие непосредственно для any P: existential-тип скрывает конкретный тип, а соответствия протоколам принадлежат конкретным номинальным типам или generic-типам с явно проверяемыми ограничениями. Если такое поведение нужно сохранить после стирания типа, применяют type erasure или объявляют соответствие для собственного wrapper-типа.
Протоколы отделяют требования к поведению от конкретной реализации, а existential-типы позволяют хранить значение неизвестного конкретного типа за интерфейсом протокола. Это удобно для гетерогенных коллекций, но при упаковке теряется часть статической информации о типе, включая конкретные associated types.
Conditional conformance решает другую задачу: оно позволяет выразить зависимость соответствия generic-типа от проверяемых ограничений его параметров. Для этого Swift должен знать тип, к которому фактически прикрепляется соответствие, чего нет у произвольного скрытого содержимого any P.
Пусть P имеет associatedtype, а некоторый протокол Q должен быть доступен только для тех реализаций P, которые удовлетворяют дополнительному условию. После преобразования значения в any P конкретный тип и его associatedtype уже не являются частью статического типа existential-значения.
Попытка объявить соответствие самого any P привела бы к неоднозначности: разные скрытые типы могли бы по-разному соответствовать Q. Нельзя выбрать одно условное соответствие для контейнера, если его условие зависит от информации, скрытой внутри этого контейнера.
Соответствия в Swift являются свойствами конкретных типов. Для обычного типа соответствие объявляется напрямую, а для generic-типа — условно, если компилятор может доказать ограничения параметров:
Здесь соответствие принадлежит Holder<T>, и условие T: P проверяется статически. Это не означает, что любой existential any P автоматически соответствует P или Q: existential представляет упаковку значения, а не сам скрытый конкретный тип как generic-параметр.
Если все реализации P должны поддерживать Q, правильнее объявить наследование протокола — protocol P: Q. Если соответствие требуется только отдельным concrete-типам, его объявляют для этих типов. Если значение нужно хранить после стирания типа, создают собственный type-erased wrapper, который явно реализует нужные требования и фиксирует подходящее представление associated type.
Главный компромисс такой: generic-код сохраняет связь со скрытым конкретным типом и его associated types, но требует однородности параметра; existential-код удобнее для хранения разных реализаций, однако теряет часть статических гарантий и не может динамически выбирать conformance для каждого скрытого типа.
Сервис принимает разные источники данных, соответствующие P, и должен помещать их в одну коллекцию. Разработчик пытается сделать any P соответствующим Q только для источников с нужным Output.
Вариант с условным соответствием самого existential-типа невозможен: Swift не прикрепляет такое соответствие к скрытому конкретному типу. Передача значений напрямую через generic-функции сохраняет статическую информацию, но не решает задачу хранения разнотипных источников в одной коллекции.
Выбранное решение — type-erased wrapper с заранее определённым erased-представлением результата, например Any или отдельным базовым протоколом. Wrapper сам соответствует Q, потому что его поведение и типы уже определены явно. Это добавляет слой косвенного вызова и иногда требует преобразований типов, зато делает контракт коллекции однозначным и предсказуемым.
Вопрос: означает ли any P наличие соответствия скрытого типа протоколу P?
Ответ: нет, existential-значение содержит значение типа, соответствующего P, но сам тип any P не следует автоматически считать generic-типом T: P. Это принципиальное различие между упаковкой значения и generic-параметром, для которого компилятор сохраняет конкретный тип.
Вопрос: можно ли решить задачу, объявив соответствие в расширении самого протокола P?
Ответ: расширение протокола может предоставить реализации его требований и дополнительные методы, но не превращает P или any P в условно соответствующий произвольному протоколу Q в зависимости от скрытого associated type. Если соответствие обязательно для всех реализаций, его задают через наследование протокола; если только для части типов, соответствие объявляют на concrete- или generic-типе.
Вопрос: почему type erasure может соответствовать Q, если any P не может?
Ответ: type-erased wrapper — это отдельный номинальный тип с собственной реализацией. Он заранее выбирает, какие операции поддерживает, какое представление имеют associated types и как делегирует вызовы скрытому объекту. Поэтому его соответствие проверяется один раз для самого wrapper-типа, а не заново зависит от неизвестного типа каждого хранимого значения.