Как "генерировать" псевдоним типа закрытия в Swift?

Чтобы сделать мой код более легким для чтения, я использую псевдонимы типов в Swift для различных типов замыканий. У меня есть следующий базовый набор замыканий:

public typealias FailureClosure = (error: NSError?) -> Void
public typealias ProgressClosure = (progress: Float32) -> Void
public typealias BasicClosure = () -> Void

Я хотел бы добавить закрытие typealias, которое поддерживает общие массивы, но я не могу понять его синтаксис. Это насколько я могу получить, но я получаю ошибку времени компиляции " Использование необъявленного типа" T ""

public typealias ArrayClosure = <T>(array:[T]?) -> Void

Кто-нибудь знает, как это сделать? Или даже если это возможно?

Ответ 1

Нет, в настоящее время это невозможно. Если бы это было возможно, синтаксис, который вы ожидали бы:

public typealias ArrayClosure<T> = (array:[T]?) -> Void

Затем вы использовали бы его как ArrayClosure<Int>. Но в настоящее время это не законно.

Тем не менее, я не рекомендую эти псевдонимы типов. Они скрывают больше, чем они освещают. Сравните эту подпись:

func foo(onError: FailureClosure)

с:

func foo(onError: NSError? -> Void)

Чтобы сохранить только пару символов, вы вынуждаете вызывающего абонента угадывать, что проходит FailureClosure. Теги error или progress вам действительно не помогают (вам все равно нужно использовать progress in ...).

Один случай, который имеет большой смысл, находится вокруг progress, но я думаю, что тип, который вы хотите, есть:

public typealias Progress = Float32

Не поймите меня неправильно, тип aliasing может быть очень полезным, когда он создает новый тип. progress - это тип, который реализуется как float. Но многое из того, что вы делаете (определенно с ArrayClosure и, в меньшей степени, с другими), просто создает новый синтаксис, не создавая новый тип, и это чаще всего путает, чем полезно.


Чтобы вызвать ваш конкретный пример и почему чрезмерное использование псевдонимов типов может привести к чрезмерному усложнению дизайна:

func foo(failure: ((error: NSError?) -> ())? = nil)

Вы правы, что это действительно сложно. Для сравнения:

func foo(failure: NSError -> Void = {_ in return})

Два больших изменения здесь. Нет причин, чтобы блок сбоя принимал необязательную ошибку. Всегда передавайте ошибку (если нет ошибки, почему бы вызвать failure?). И нет причины, чтобы блок отказов был необязательным. Если вы действительно хотите значение по умолчанию, просто сделайте значение по умолчанию ничего не сделайте. Два варианта исчезли, и все потребляющие и реализующие коды упрощаются. Всегда подумайте о том, что-то абсолютно необязательно. Опционы добавляют много сложности; не добавляйте их слегка.

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

func foo(#failure: NSError -> Void) { ...  }

func foo() {
  foo(failure:{ _ in return })
}

Я просто думаю, что немного легче понять, что происходит. Но в любом случае это нормально.


EDIT (декабрь 2014): после написания Swift еще несколько месяцев, я больше полюбил подход @David в комментариях ниже, который заключается в использовании опциона для закрытия, но не для ошибки. В частности, данный синтаксис Swift необязателен (failure?()), он часто становится более ясным.

func foo(failure: (NSError -> Void)? = nil)