假设实现讲一个字符转换为 int,再取反,之后返回一个字符,相关实现如下:
from operator import neg
y = 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 False
print(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 False
print(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 -3
print(List([1,2,3]) | neg) # -1 -2 -3
print(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
评论