假设实现讲一个字符转换为 int,再取反,之后返回一个字符,相关实现如下:
from operator import negy = str(neg(int('1'))) # y = -1
复制代码
如果上述实现传入的参数是'xyz',那么上述实现会抛出异常,如果不进行异常捕获处理,那么就会导致程序崩溃。我们可以考虑,我们是否可以实现这样的一个功能,保存一个标志,判断是否存在异常,如果存在异常则捕获并返回None和设置标志为True。很显然str、neg和int函数并不具备这样的功能,此时便是 Monad 的用武之地。
在[1]文章中作者指出:
A monad is a design pattern that allows us to add a context to data values, and also allows us to easily compose existing functions so that they execute in a context aware manner.
翻译就是:Monad 是一种设计模式,它使得我们可以向数据中添加上下文,进而实现一些组合功能,从而达到可以感知上下文的方式运行。
Failure Monad
下面实现上述的 Monad,主要是添加一个 bind 方法主要实现:
从 Monad 中取出value参数
执行bind方法的入参(一个函数)
包装一个新的 Monad 并返回
class Failure: def __init__(self, value, failed = False): self.value = value self.failed = failed def get(self): return self.value def __str__(self): return ' '.join([str(self.value), str(self.failed)]) def bind(self, f): if self.failed: return self try: x = f(self.value) return Failure(x) except: return Failure(None, True)
# 测试数据print(Failure(1).bind(int).bind(neg).bind(str)) # -1 Falseprint(Failure("xyz").bind(int).bind(neg).bind(str)) # None True
复制代码
接下来我们进行一些语法改进,通过重载|运算符,达到 bind 函数的实现。
def __or__(self, f): return self.bind(f)
复制代码
那么,上述测试案例可以按照如下方式实现:
print(Failure('1') | int | neg | str) # -1 Falseprint(Failure('xyz') | int | neg | str) # None True
复制代码
反观,这种实现的结果和 Linux 的管道,形式上类似。
List Monad
List Monad 主要实现以下功能:
class List: def __init__(self, value): self.value = value def get(self): return self.value def __str__(self): return ' '.join(map(str, self.value)) def bind(self, f): y = map(f, self.value) return List(y) def __or__(self, f): return self.bind(f)
# 测试程序print(List([1,2,3]).bind(neg)) # -1 -2 -3print(List([1,2,3]) | neg) # -1 -2 -3print(List([1, 2, 3]).bind(str).bind(lambda s: s.zfill(4))) # 0001 0002 0003
复制代码
Maybe monad
在实际程序运行中,还经常出现读取到不存在值的情况,例如,查找一个不存在的值,或在尝试从数据库中读取一条根本就不存在的记录。解决这种情况常见的实现方式是将变量置为 None,但是这存在一些问题:
程序中需要一系列if not x的语句,对值不存在的情况进行处理
有时 None 并不代表值不存在,而是表示计算中遇到了错误,或者其他情况。
有时 None 是该字段一个有效状态,且不表示特殊意义,例如不存在的状态等。
基于此,提供 Just Monad 和 Nothing Monad 分别表示值存在和不存在两种情况。
Just Monad
class Just(): def __init__(self, value): self.value = value def get(self): return self.value def bind(self, f): result = f(self.value) return Just(result) def __str__(self): return 'Just(' + str(self.value) + ')' def __or__(self, f): return self.bind(f)
# 测试程序print(Just(3) | neg) # Just(-3)
复制代码
Nothing Monad
此时,无value,所以bind函数的返回值总是Nothing()。
class Nothing(): def __init__(self): pass def get(self): return None def bind(self, f): return Nothing() def __str__(self): return 'Nothing()' def __or__(self, f): return self.bind(f)
# 测试程序print(Nothing().bind(neg))# Nothing()
复制代码
参考资料
Monads in Python
More Monads in Python
评论