Подтвердить что ты не робот

Какое точное ограничение на общие связанные значения в списках Swift?

Я пытаюсь понять точные пределы перечислений с общими связанными значениями в Swift.

Вы можете подумать, что они поддерживаются, так как Optional - такой тип. Вот код, определяющий Optional в стандартной библиотеке Swift:

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case None
    case Some(T)
// ...
}

Кажется, что член Some имеет связанное значение типа переменной T, правильно?

Однако в книге Функциональное программирование в Swift указано (стр. 87), что такие типы не поддерживаются:

Мы хотели бы определить новое перечисление, которое является общим в результат, связанный с успехом:

enum Result<T> {
    case Success(T)
    case Failure(NSError) 
} 

К сожалению, общие связанные значения не поддерживаются текущим компилятором Swift.

И действительно, если вы введете этот фрагмент в компилятор, вы получите сообщение об ошибке (error: unimplemented IR generation feature non-fixed multi-payload enum layout).

Итак, что здесь происходит? Это просто, что он не поддерживается вообще, но поддерживается для Optional как особый случай? Есть ли способ увидеть, как опция получает эту специальную поддержку? Или если другие стандартные типы библиотек также получают специальную поддержку?

4b9b3361

Ответ 1

Этот ответ устарел в Swift 2. Пожалуйста, см. ответ rickster для обновлений Swift 2.

Ваши комментарии верны. Вы не можете иметь несколько случаев со связанными данными, если какой-либо из них имеет неизвестный размер. Типы значений могут быть любого размера (поскольку они скопированы). Типы ссылок (например, объекты) имеют известный размер, поскольку они хранят указатель.

Типичным решением для этого является создание дополнительного класса-оболочки для хранения общего типа, как это делает книга FP. Все называют его Box по соглашению. Есть основания надеяться, что команда Swift исправит это в будущем. Как вы заметили, они называют это "нереализованным" не "неподдерживаемым".

Типичная реализация Box:

final public class Box<T> {
  public let unbox: T
  public init(_ value: T) { self.unbox = value }
}

Ответ 2

В Swift 2 (в настоящее время в бета-версии Xcode 7) нет ограничений на связанные значения. Итак, не стесняйтесь танцевать так, как это:

enum YouCanGoWith<T, U> {
    case This(T)
    case That(U)
    case Us
}

Теперь, если вы ищете переименование типа "Успех или ошибка", вы можете остановиться и подумать о том, почему... потому что Swift 2 также приносит новую модель обработки ошибок. Таким образом, вам не нужен такой тип, как возвращаемое значение ваших функций - вы можете просто объявить его так:

func walkWith(rhythm: Bool) throws -> Place { /* ... */ }

... и если ваша функция завершается успешно, вызывающий объект всегда получает (не факультативный) Place для перехода. И - отдельно от использования результата - вызывающий решает, как обрабатывать, проглатывать или распространять ошибку.

Подробнее об этом см. Обработка ошибок на языке Swift. Посмотрите внимательно - синтаксис немного похож на модель исключения, которую вы видите на некоторых других языках, но ошибки Swift - совсем другое вид животного.

(Конечно, модель throws специфична для синхронных вызовов.Если вместо этого вы объявляете обратные вызовы для асинхронных процессов, где закрытие обратного вызова получает либо результат успешной асинхронной работы, либо ошибку - Тип ошибки по-прежнему полностью подходит.)