Swift中用于像我这样的傻瓜的功能组合-第3部分
如您所见, Array
每个值都由传递给map
的函数转换。
我经常看到的一个错误是,认为 map
是用于集合的操作 。 它对于集合当然是有用的,但是将地图视为对集合进行操作的一种方式将您限制为集合类型。 在Swift中,Swift标准库中有一些针对Optional
和Result
类型的map
函数,一旦您开始认识到它们在哪里有用,就可以为系统中定义的许多其他类型定义map
。 Optional
和Result
都不是集合,并且它们都是可map
。 两者还具有无法映射的状态。 当Optional
为nil
,由于没有值,它无法将提供的转换函数应用到map
函数,因此map
跳过执行转换函数,而Optional
值保持为nil
。 这种行为非常类似于Swift可选绑定的工作原理,但是没有所有样板。 首先,使用可选绑定而不是map
的示例:
接下来,是一个使用Optional
的map
实现来完成相同任务的示例。
最后,一个使用“可选”的“ 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) -> Result
和Array
上的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组成
通过使用map
和flatMap
将操作链接在一起,您可以创建Scott Wlaschin所谓的“面向铁路的编程”。 他将这些链接的功能比作两条轨道的铁路,在此过程中,只要按照map
和flatMap
操作方式而无需使用分支,就可以将内容路由到故障或零轨道。 让我们来看一些说明这种编程风格的示例。
对于此示例,我们想从事件URL字符串Dictionary
中查找getEvents
服务的URL值。 该服务将发回[“index” : “event name”]
值的JSON字典。 我们将查找与索引“ 1”相对应的事件名称。 如果字典中不存在索引,则最终结果将是名称或nil
。
现在,将其与使用doom金字塔来编程相同步骤序列的命令式风格进行比较。
TL; DR结论
功能组合模式严重依赖于Functor(认为可map
)和Monads(认为FlatMap可用)。 两者都是包装器类型,它们提供了围绕另一种类型的结构。 Array
, Optional
和Result
是Monads包装类型的示例。 一旦开始了解如何使用它们,您将定义自己的Monad或意识到已经有Monad在等待向其添加map
和flatMap
函数。
使用Monads,您可以以简洁而富于表现力的方式将操作链接在一起,从而减少样板代码,例如if let… else…
一些Monad,例如Optional
或Result
,有两种可能的状态:一种状态是包装类型,而另一种状态是缺少包装类型。 这些Monad为其映射操作提供了一种两轨设计模式,如果不存在包装类型,则在操作链中会绕过步骤。 这已被比作两条轨道的铁路,其中一条轨道用于成功地链接后续操作,而另一条轨道则用于在一个步骤失败时绕过其余的操作链。
下一主题:咖喱
在我的下一篇文章中,我想深入探讨curring,以及如何将其用于将自由函数链接在一起,这些自由函数是标准库的一部分,也是您自己的类型定义的一部分。 这应该为您的未来项目打开广阔的功能组合世界。
与往常一样,如果喜欢这篇文章,请鼓掌;如果您想查看本系列的其余部分,请遵循。 或者,您可以访问我的页面以查看我涵盖的更多主题。