Swift中用于像我这样的傻瓜的功能组合-第3部分

如您所见, Array每个值都由传递给map的函数转换。

我经常看到的一个错误是,认为 map 是用于集合的操作 。 它对于集合当然是有用的,但是将地图视为对集合进行操作的一种方式将您限制为集合类型。 在Swift中,Swift标准库中有一些针对OptionalResult类型的map函数,一旦您开始认识到它们在哪里有用,就可以为系统中定义的许多其他类型定义mapOptionalResult都不是集合,并且它们都是可map 。 两者还具有无法映射的状态。 当Optionalnil ,由于没有值,它无法将提供的转换函数应用到map函数,因此map跳过执行转换函数,而Optional值保持为nil 。 这种行为非常类似于Swift可选绑定的工作原理,但是没有所有样板。 首先,使用可选绑定而不是map的示例:

接下来,是一个使用Optionalmap实现来完成相同任务的示例。

最后,一个使用“可选”的“ map”实现的示例显示,当“可选”为nil时,跳过了转换。

以类似的方式,新的Swift 5 Result类型中包装的值如果为.failure ,则无法进行转换,因此将跳过转换函数,并且一系列操作的结果将为.failure 。 这在组成一系列动作时非常有用,在这些动作中,您仅希望在前面的步骤成功后才执行该动作。 我将回到这一点,并在本文中进一步扩展。 以下是一些片段,展示了map如何在Result类型上工作:

所有这些的最终结果是,您可以将依赖于先前操作结果的操作链接在一起,而无需使用嵌套的ifs。 map函数将正确调用或跳过转换。 但是,这些示例中潜伏着一个问题。 如果Optional map中的转换函数产生另一个Optional ,则结果为Optional<Optional> ,即嵌套的Optional 。 完全有效,但很难处理。 在以下示例中查看有效内容的类型:

有一种避免这种嵌套的解决方案,在Swift(和其他(但不是全部)功能语言)中称为flatMap

Monad简介—认为可映射和可平面映射

和以前一样,请随时在线搜索Monad的完整定义。 范畴论的定义很难解析。 为了便于讨论,您需要知道的是Monad是包装类型,它具有flatMap函数和map函数。 flatMap函数将特定类型的转换函数(或态射)作为参数,并将该转换应用于包装的一个或多个值。 特定的转换是一种输入类型与包装类型相同且输出类型与Monad本身相同但包装一个转换值的转换。 换句话说,形状为(A) -> M ,其中M表示为FlatMapped的Monad类型。 换句话说, Optional上的flatMap期望形状为(A) -> Optional的转换函数, Result上的flatMap期望形状为(A) -> Result的转换函数(A) -> ResultArray上的flatMap期望形状为(A) -> [B]的转换函数。 你明白了。

在前面的示例中,关闭{ siteUrl in try? String(contentsOf: siteUrl) } { siteUrl in try? String(contentsOf: siteUrl) }的形状为(URL) -> Optional 。 并且由于Optional map将转换后的值包装在Optional ,因此结果是嵌套的Optional<Optional> 。 但是,在flatMap上使用Optional ,结果不会嵌套到另一个Optional而您只是获得Optional 。 这是使用flatMap的相同链:

使用Map和FlatMap组成

通过使用mapflatMap将操作链接在一起,您可以创建Scott Wlaschin所谓的“面向铁路的编程”。 他将这些链接的功能比作两条轨道的铁路,在此过程中,只要按照mapflatMap操作方式而无需使用分支,就可以将内容路由到故障或零轨道。 让我们来看一些说明这种编程风格的示例。

对于此示例,我们想从事件URL字符串Dictionary中查找getEvents服务的URL值。 该服务将发回[“index” : “event name”]值的JSON字典。 我们将查找与索引“ 1”相对应的事件名称。 如果字典中不存在索引,则最终结果将是名称或nil

现在,将其与使用doom金字塔来编程相同步骤序列的命令式风格进行比较。

TL; DR结论

功能组合模式严重依赖于Functor(认为可map )和Monads(认为FlatMap可用)。 两者都是包装器类型,它们提供了围绕另一种类型的结构。 ArrayOptionalResult是Monads包装类型的示例。 一旦开始了解如何使用它们,您将定义自己的Monad或意识到已经有Monad在等待向其添加mapflatMap函数。

使用Monads,您可以以简洁而富于表现力的方式将操作链接在一起,从而减少样板代码,例如if let… else… 一些Monad,例如OptionalResult ,有两种可能的状态:一种状态是包装类型,而另一种状态是缺少包装类型。 这些Monad为其映射操作提供了一种两轨设计模式,如果不存在包装类型,则在操作链中会绕过步骤。 这已被比作两条轨道的铁路,其中一条轨道用于成功地链接后续操作,而另一条轨道则用于在一个步骤失败时绕过其余的操作链。

下一主题:咖喱

在我的下一篇文章中,我想深入探讨curring,以及如何将其用于将自由函数链接在一起,这些自由函数是标准库的一部分,也是您自己的类型定义的一部分。 这应该为您的未来项目打开广阔的功能组合世界。

与往常一样,如果喜欢这篇文章,请鼓掌;如果您想查看本系列的其余部分,请遵循。 或者,您可以访问我的页面以查看我涵盖的更多主题。

Interesting Posts