flatMap API合约如何转换可选输入到非可选结果? [英] How flatMap API contract transforms Optional input to Non Optional result?

查看:108
本文介绍了flatMap API合约如何转换可选输入到非可选结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是Swift 3.0.2中flatMap的契约。

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

如果我将 String?] flatMap返回[String]

  let albums = [Fearless,nil,现在讲话,零,红色] 
让结果= albums.flatMap {$ 0}
类型(of:result)
// Array< String> .Type

这里 ElementOfResult 变成 String ,为什么不是字符串??泛型类型系统如何从表达式中去除可选部分?

解决方案

当您使用标识转换 {$ 0} ,编译器会推断出 ElementOfResult?(转换结果)等于元素(变换的参数)。在这种情况下, Element String?,因此 ElementOfResult? == 字符串?。这里不需要可选的促销,因此 ElementOfResult 可以推断为 String



因此,在这种情况下, flatMap(_:)返回一个 [String] / p>

在内部,从闭包返回 ElementOfResult? ElementOfResult 只是通过有条件地解包可选项来完成的,如果成功,则将解包值附加到结果中。你可以在这里看到确切的实现




作为附录,请注意,正如Martin指出的那样,闭包仅在单一语句关闭(请参阅此相关错误报告)。对此的推理是由乔丹罗斯给出 in这个邮件列表讨论
$ b


Swift的类型推断当前是以语句为导向的,所以没有简单的方法可以做[multiple-语句结束]推理。这至少部分是编译时间问题:Swift的类型系统允许比Haskell或OCaml更多的可能转换,因此解决整个多语句函数的类型不是一个微不足道的问题,可能不是一个容易处理的问题。

这意味着对于传递给诸如 map(_:) flatMap(_:)(其中结果类型是一个通用占位符),您必须显式注释闭包的返回类型,或者该方法返回自身。



例如,这不会编译:

  //错误:无法推断复杂的闭包返回类型;添加显式类型以消除歧义。 
let result = albums.flatMap {
print($ 0 as Any)
return $ 0
}

但是这些可以:

  //明确注释[ElementOfResult]为[String ]  - 因此ElementOfResult ==字符串。 
让结果:[String] = albums.flatMap {
print($ 0 as Any)
return $ 0
}

  //明确注释ElementOfResult?是字符串? - 因此ElementOfResult ==字符串。 
let result = albums.flatMap {element - >串? in
print(element as Any)
return element
}


This is the contract of flatMap in Swift 3.0.2

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

If I take an Array of [String?] flatMap returns [String]

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

Here ElementOfResult becomes String, why not String? ? How is the generic type system able to strip out the Optional part from the expression?

解决方案

As you're using the identity transform { $0 }, the compiler will infer that ElementOfResult? (the result of the transform) is equivalent to Element (the argument of the transform). In this case, Element is String?, therefore ElementOfResult? == String?. There's no need for optional promotion here, so ElementOfResult can be inferred to be String.

Therefore flatMap(_:) in this case returns a [String].

Internally, this conversion from the closure's return of ElementOfResult? to ElementOfResult is simply done by conditionally unwrapping the optional, and if successful, the unwrapped value is appended to the result. You can see the exact implementation here.


As an addendum, note that as Martin points out, closures only participate in type inference when they're single-statement closures (see this related bug report). The reasoning for this was given by Jordan Rose in this mailing list discussion:

Swift's type inference is currently statement-oriented, so there's no easy way to do [multiple-statement closure] inference. This is at least partly a compilation-time concern: Swift's type system allows many more possible conversions than, say, Haskell or OCaml, so solving the types for an entire multi-statement function is not a trivial problem, possibly not a tractable problem.

This means that for closures with multiple statements that are passed to methods such as map(_:) or flatMap(_:) (where the result type is a generic placeholder), you'll have to explicitly annotate the return type of the closure, or the method return itself.

For example, this doesn't compile:

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

But these do:

// 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
}

这篇关于flatMap API合约如何转换可选输入到非可选结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆