Переменные типа void

Итак, я знаю, что Void используется, чтобы указать, что функция не принимает или не возвращает значение. В Swift Void на самом деле является псевдонимом типа для пустого кортежа ().

Интересно (в бета-версии 6) вы даже можете объявить переменные типа Void:

var x: Void
println(x)
x = test()

func test() {}

Все эти заявления являются законными. println() печатает "()".

До сих пор я не мог понять, для чего еще это можно использовать. Зачем вам нужна переменная, которая не может иметь значения? Кто-нибудь знает практическое применение этого, или это только одна из причуд Swift?

Ответ 1

Примечание. Это перекрестный вызов мой ответ в вопросе перекрестного сообщения в форумах Apple Dev.

Это потому, что Swift кажется немного академически благоприятным (это один из Swift plus для меня!:-)).

Математическая функция - это отношение между некоторыми входами (аргументами) и некоторыми выходами (возвращаемыми значениями). Вместо определения целого класса (например, "подпрограмма" или "процедура" ), для которой это не применяется (без вывода, в частности), Swift просто определяет "процедуру" как отношение с некоторым набором входов (возможно, пустым) и пустой набор выходов.

Есть некоторые преимущества для него, хотя и необычные (по крайней мере, до тех пор, пока функциональное программирование не станет более доминирующим в языке и сообществе). Маленький пример:

infix operator ~~ { precedence 10 associativity right }
func ~~(l: A -> B, r: A) -> B {
     return l(r)
}

let productsSoldIn2014 = productOfSales ~~ salesInYears ~~ [2014]
let salespersonsInvolvedIn2014Sales = salespersonsOfSales ~~ salesInYears ~~ [2014]

fireSalespersons ~~ salespersonsOfSales ~~ salesInYears ~~ [2014]   // Awful companies only.

func salesInYears(_ y: [Int]) -> [Sale] { … }
func productsOfSales(_ s: [Sale]) -> [Product] { … }
func salespersonsOfSales(_ s: [Sale]) -> [SalesPerson] { … }
func fireSalespersons(_ s: [SalesPerson]) -> () { … }               // The appended arrow and null tuple can be left out.

Я определил оператор "цепочки" ~~ (подумайте о цепочке изгиба), который справа налево выталкивает значение через цепочку. С некоторыми соответствующими названными функциями мы можем декларативно определять нужные нам значения. (С ленивыми коллекциями, а не массивами, мы подходим к парадигмам Хаскелла. Если я не ошибаюсь, ~~ здесь присутствует идентификатор monad. Или это просто оператор композиции.)

Третий оператор (после двух дат) запускает продавцов, которые продали что-либо в 2014 году. Обратите внимание, что ~~ хочет вернуть значение "fireSalespersons" (чтобы продолжить цепочку слева, что здесь не present), который в Swift действителен - это просто нулевой кортеж, и мы его отбрасываем. Нет причин, по которым функции без возвращаемого значения не должны работать с оператором (без перегрузки).

Кстати, это также верно:

increaseCEOBonus(35000) ~~ fireSalespersons ~~ salespersonsOfSales ~~ salesInYears ~~ [2014]      // Really awful companies only.
//         () -> ()  ~~  [Salesperson] -> ()  ~~  [Sales] -> [Salesperson] …

func increaseCEOBonus(amount: Int)() { … }

В этом примере самая левая цепочка не имеет значения - она ​​по-прежнему остается справа, но нет никаких значений, переданных влево. Я бы не рекомендовал это использовать, но лучше просто перечислить каждую из них на отдельной строке.

Чтобы вернуться к вашему вопросу, поскольку "ничего" можно использовать (через аргументы), было бы глупо явно запрещать "ничего" назначать переменным-аргументам переменные.

P.S.: Прежде чем кто-нибудь спросит: да, я хотел иметь оправдание, чтобы сделать надуманный пример; это было просто проверить мою способность писать сложные типы.

Ответ 2

Я наткнулся на практический пример, где это полезно при работе с дженериками. У меня есть общий класс, который создает обертку вокруг задач async. Один из методов класса позволяет публиковать результат.

Перейдем к упрощенному примеру:

class Foo<T>  {

    var x : T?

    func finishWith(result:T) {
        x = result
        // do more stuff here as a side effect of posting the result
    }
}

Вопрос в том, нужен ли вам результат Void, т.е.

var foo = Foo<Void>()

Как вы называете метод finishWith:result?

Вы можете создать переменную со значением void или использовать '()' в качестве значения буквенного значения void:

foo.finishWith(result: ())

Ответ 3

Вероятно, немного более полезным является тип Void?, который можно использовать для хранения, например. a try?, который не возвращает значение, но вы все еще хотите проверить успех:

let someString = "foo"
let saved: Void? = try? someString.write(toFile: "somePath", atomically: false, encoding: .utf8)
if (saved == nil) {
    print("didn't save")
}