Abstract: 继续阅读 Scala with Cats, 开始学习 Functor。前面的 Monoid 和 SemiGroup 封装的是一种合并或者相加的能力,Functor 就是封装的一种进行连续操作的能力。与 Monoid 和 SemiGroup 紧密联系的操作是 fold,而 Functor 就是 map 或者是 flatMap。
数学背景
直观理解
如果不严格的讲,所有的带有 map 操作的都可以称之为 Functor,所以我们熟悉的 List,Option,Either 都是 Functor。
比如 List,通过 map 操作,我们针对其中的元素进行一个链式操作,每个 map 操作后,仍然返回的是一个 List。
1 | List(1, 2, 3). |
除了这些数据类型定义以外,对于一元方程来说也是 Functor。
- start with
X => A; - supply a function
A => B; - get back
X => B.
How does this relate to our general pattern of sequencing operations? If we think about it, function composition is sequencing. We start with a function that performs a single operation and every time we use
mapwe append another operation to the chain. Callingmapdoesn’t actually run any of the operations, but if we can pass an argument to the final function all of the operations are run in sequence.
定义
Every example we’ve looked at so far is a functor: a class that encapsulates sequencing computations. Formally, a functor is a type
F[A]with an operationmapwith type(A => B) => F[B].
Functor :是一个类(类型),这个类封装了一种能力,这种能力是进行连续运算。
数学定义: Functor 是一个 F[A] 类型和一个 map 操作,这个 map操作的类型定义为 (A => B) => F[B]。

Cat Functor 定义
1 | package cats |
Functor 的 Scala 定义十分简单,是一个带有类型参数的 trait,同时包含一个方法 map,map 方法定义两个类型参数 A 和 B,其中输入的参数是类型 A 的函数 F,而输出的是类型 B 的函数 F。
这里 Functor[F[_]] 是一个高阶类型。
Functor 规范
- 如果传入的是
identity函数,那么等价于啥都没做。 - 交换操作的顺序结果应该是一致的。
Identity: calling
mapwith the identity function is the same as doing nothing:
1 | fa.map(a => a) == fa |
Composition:
mappingwith two functionsfandgis the same asmappingwithfand thenmappingwithg:
1 | fa.map(g(f(_))) == fa.map(f).map(g) |
高阶类型(Higher Kinds)和类型构造函数(Type Constructors)
Kinds 可以理解为类型的类型,直观的看就是在类型中定义的空缺的孔,或者理解为参数。通过在类型中是否有参数 把类型分为常规类型和类型构造器。
比如: List 就是一个带有一个参数的类型构造器,我们可以传入一个类型作为参数,从而将这个 List 转化为常规类型,如 List[Int] 和 List[A]。这里虽然 List[A] 中 A 可以是任意类型,但是仍然是一个确定的参数,所以它是泛型,而不是类型构造器,List 本身才是构造器。
List // type constructor, takes one parameter
List[A] // type, produced using a type parameter
我们可以用函数和值来对等的了解上面类型的概念:
math.abs // function, takes one parameter
math.abs(x) // value, produced using a value parameter
- 类型构造器 类似于 函数
- 类型 类似于 值
Scala 使用下划线来定义类型构造器:
1 | // Declare F using underscores: |
基于这个类型构造器,我们可以看到 Cats定义的 Functor 允许我们创建单一参数类型的类型构造器 的实例。这个实例就会具有 map 的能力。
the Cats definition of Functor allows us to create instances for any single-parameter type constructor, such as List, Option, Future, or a type alias such as MyFunc.
Cats Functors 使用
基础类型
这个用法仅仅作为理解使用,因为很多类型已经具有了 map 操作,使用 Functor 并没有太多意义。下面的例子使用的 Object 工厂方法进行 map 操作。
1 | import scala.language.higherKinds |
Lift 方法
Functor also provides the
liftmethod, which converts a function of typeA => Bto one that operates over a functor and has typeF[A] => F[B]:
这个方法称为提升,顾名思义,将原来的函数提升一个层次,提升一个阶,直观上看就是再包一层函数。
1 | val func = (x: Int) => x + 1 |
原函数是一个整型映射到整型加一,通过带有 Option 类型参数的 lift 操作,编程了 Option(整型) 映射到 Option(整型+1) 的函数。
Syntax
使用 Cats,仍然推荐使用 Syntax,代码简洁。
1 | import cats.instances.function._ // for Functor |
和 Monoid 不同,这里没有定义操作符 (如 |+|),而是直接使用 map。这里一定要区分开那些是类型原生的 map 方法,那些是 Functor 提供的 map。
自定义 Functor 实例
参考 Type Class 模式,我们同样可以自定义任意类型的 Functor,只需要实现其 map 操作。比如我们自己定义一个 Future 的 Functor
1 | import scala.concurrent.{Future, ExecutionContext} |
案例: 二叉树
1 | sealed trait Tree[+A] |
逆变和不变
总结
Functor 表现的是一系列连续的操作。主要有三种类型
- 常规协变 Functor
- 逆变 Functor
- 不变 Functor