2017-02-13 5 views
1

Это договор flatMap в Swift 3.0.2Как преобразовывается договор API FlatMap Дополнительный ввод для результата Non Optional?

public struct Array<Element> : RandomAccessCollection, MutableCollection { 
    public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] 
} 

Если взять массив [String?] flatMap возвращается [String]

let albums = ["Fearless", nil, "Speak Now", nil, "Red"] 
let result = albums.flatMap { $0 } 
type(of: result) 
// Array<String>.Type 

Здесь ElementOfResult становится String, почему не String?? Как система родового типа может вырезать необязательную часть из выражения?

ответ

2

Как вы используете тождественное преобразование { $0 }, компилятор будет считать, что ElementOfResult? (результат преобразования) эквивалентно Element (аргумент преобразования). В этом случае Element - String?, поэтому ElementOfResult? == String?. Здесь нет необходимости в дополнительном продвижении, поэтому ElementOfResult можно определить как String.

Поэтому flatMap(_:) в этом случае возвращает [String].

Внутренне это преобразование из возврата закрытия ElementOfResult? в ElementOfResult выполняется просто путем условного разворачивания по желанию, и в случае успеха развернутое значение добавляется к результату. Вы можете увидеть exact implementation here.


В качестве дополнения, отметим, что as Martin points out, затворы только участвуют в умозаключения типа, когда они одной выписки закрытия (см this related bug report). Смысл этого был дан Jordan Rose in this mailing list discussion: определение типа

Свифта в настоящее время заявления-ориентированное, так что нет простого способа сделать [множественное заявление закрытия] умозаключение. Это, по крайней мере, частично связано со временем компиляции: система типов Swift допускает гораздо больше возможных преобразований, чем, скажем, Haskell или OCaml, поэтому решение типов для всей функции с несколькими операторами не является тривиальной проблемой, возможно, не приемлемой проблемой.

Это означает, что для закрытия с несколькими заявлениями, которые передаются такими методы, как map(_:) или flatMap(_:) (где тип результата является общим заполнителем), вам придется явно аннотирование типа возвращаемого закрытия, или метод возвращает себя.

Например, это не компилируется:

// error: Unable to infer complex closure return type; add explicit type to disambiguate. 
let result = albums.flatMap { 
    print($0 as Any) 
    return $0 
} 

Но это сделать:

// explicitly annotate [ElementOfResult] to be [String] – thus ElementOfResult == String. 
let result: [String] = albums.flatMap { 
    print($0 as Any) 
    return $0 
} 

// explicitly annotate ElementOfResult? to be String? – thus ElementOfResult == String. 
let result = albums.flatMap { element -> String? in 
    print(element as Any) 
    return element 
} 
+2

Возможно подчеркнуть, что тип возвращаемого значения выводится автоматически * закрытие одного выражения. * 'albums.flatMap {e in print (e); return e} 'не компилируется. –

+0

@MartinR Yup, хорошая точка - спасибо за упоминание :) – Hamish

+0

Хотя обременительно и удивительно, из отчета об ошибке сообщается, что на самом деле это не ошибка! – matt

Смежные вопросы