Maybe,Monad
C++有多种方式来表示某个”对象”是否有值:
使用
nullptr
来表示没有值使用智能指针(例如,
shared_ptr
). 它提供了检测手段std::optional<T>
是一种库解决方案. 它可以存储T
值或std::nullopt
.
如果我们打算使用nullptr
的方法, 假定我们的领域对象定义了一个Person
对象, 它可能有一个Address
成员属性, 而后者有一个可选的house_name
属性.
至少在英国, 有些房子是有名字的. 例如, 如果你买一座城堡, 它的地址可能不是”123 Londo Road”, 而是”Montefiore Castle”, 而这也是它的地址.
当然, 不是所有的house都有name.
1 | struct Address |
我们关心的是, 比如, 如何安全地打印出某个人的house name, 如果它存在的话.
使用”传统”的C++, 我们可能需要这样写:
1 | void print_house_name(Person* p) |
我们称这种现象为drill down into an object’s struct.
使用一种称为Maybe Monad的模式
首先定义类型Maybe<T>
. 它作为一个临时对象参与到”drill down”的过程中去:
1 | template <typename T> |
到目前, Maybe
似乎没有什么大用, 我们也无法从Person* p
来构造出Maybe(p)
, 因为无法从传递给构造函数的参数中推导出类模板参数. 为此, 还需要定义一个全局辅助函数, 因为函数是可以推到模板参数的:
1 | template <typename T> |
接下来要做的就是为Maybe
定义成员函数:
- 如果
context!=nullptr
, 就继续深入范根对象, 或者 - 如果
context==nullptr
, 就什么也不做
1 | template <typename Func> |
这是一个高阶函数的例子. 它接受一个参数作为参数, 并返回一个指针, 它可以被封装到另一个Maybe
里面. 这样, 我们可以链式调用With()
.
同样可以定义另一个成员函数, 它仅仅是激活给定义的作用在context
上的函数, 而不会改变context
.
1 |
|
现在, 就可以使用它:
1 | void print_house_name(Person *) |