Swift Tips 020 - Combining a sequence of functions

每天了解一点不一样的 Swift 小知识

代码截图

小笔记

这段代码在说什么

代码截图里声明了一个 + 操作符,该操作符的两侧均为函数类型,且能够将 + 操作符的左参函数的返回值作为右参函数的入参。从而实现了一种类似“链式调用”的效果。

例如下面的代码:

try (determineTarget + build + analyze + output)()

等价于

try (output( analyze ( build ( determineTarget ()))))

为什么能这么做

运算符非常基础,大多数语言都将它们作为编译器(或解释器)的一部分进行处理。 但是 Swift 编译器并不对大多数操作符进行硬编码,而是将这部分工作留给了Swift 标准库,通过它来提供所有常见的操作符。

也正是这种微妙的差异为 Swift 打开了一扇通往“自由”的道路。在 Swift 中,我们不用被预定义的运算符所限制,可以自由地定义中缀、前缀、后缀和赋值运算符,这些自定义运算符在代码中可以像预定义的运算符一样使用,你甚至可以扩展已有的运算符。

说到底,运算符的本质只是一个函数而已。而在 Swift 的世界里,函数可是一等公民呀!

关于自定义运算符的更多内容,可阅读 The Swift Programming Language 一书中的 自定义运算符 章节。

这样做的好处

函数式编程在 Swift 领域里经常被提及,而今天的这个主题也与函数式编程息息相关。代码截图里定义的 + 操作符,很像函数式编程里提及的管道操作符(pipe operator)。

而同样功能的操作符,我们已经可以在 JavaScript,F# 里预置的操作符中看到(|> 操作符)。

关于 JavaScript 和 F# 的管道操作符,可以阅读下面的资料:

  1. JavaScript 的管道操作符文档
  2. F# – Pipe Forward and Pipe Backward

这种类型的操作符允许我们以一种易读的方式去对函数进行链式调用。从另一个角度来看,管道操作符是单参数函数调用的语法糖,它允许我们这样执行一个调用(下面的例子使用的 JavaScript):

const double = (n) => n * 2;
const increment = (n) => n + 1;

// 没有用管道操作符
double(increment(double(5))); // 22

// 用上管道操作符之后
5 |> double |> increment |> double; // 22

虽然这种方式第一眼看上去有点懵,但熟悉了之后,在链式调用多个函数时,使用管道操作符还是改善了代码的可读性,毕竟从左到右的阅读方式更符合我们的认知。