如何获知函数执行是否成功,以及它的返回值?不同的语言和技术体系,给出的策略是不同的。
Java 的方案比较简单,抛异常表示错误,执行成功的话 return。
很多 C 技术栈,比如 windows api,采用的是以整数返回值表示是否发生错误,和错误代码的方式,函数执行结果则放在一个函数参数中,这个参数通常定义为某一个struct的二阶指针。
Go 语言推荐的方式是让函数返回两个参数,即总是 `err, result` 形式,调用函数的代码通过判断 err 是否为 nil 来判断是否发生了错误。
在函数式编程领域,Haskell 的做法是提供一个 Either 类型,当 Either 的状态为 Left 时,携带错误信息,当它状态为 Right 时,携带的是执行成功后得到的结果。这样我们就可以在 Either 上实现 flatMap 和map,使其状态成为一个 monad,因为这个monad只会在 Right 状态下执行,就区分了正常值和错误。当然,Left也可以实现出对应的错误处理monad。这种模式可以一致的将函数执行结果的状态管理和时间维度上的异步任务,空间维度上的列表、数组等都抽象为monad模型,获得一致的处理形式。
Scala 同样实现了自己的 Either\[E, T\] 类型,但是在 JVM 平台上,异常通常会要求实现为 Throwable 的子类,因此实际上完全可以将执行状态抽象成 Either[Throwable, T],这样我们可以省掉 Left 的类型参数,于是得到了一个只有一个类型参数的泛型返回值抽象,这就是 Scala 的 Try[T] 类型。
Try. 类型可以很好的与平凡的异常抛出/捕获机制结合。当从一个 Try 类型对象中 get 值的时候,如果其携带的是一个错误,就会抛出异常,这就回到了 try catch 模式,反之也可以将一个平凡的函数调用直接封装为一个 Try 。它可以通过 pattern match 调用方便的处理不同状态,也实现了 flatMap、map、filter 等 monad 操作,这使得它与 scala 生态中的各种数据结构和异步模型有了统一的使用范式。
这种模式也可以用在其它编程语言中,包括 Java 的一些框架,也在模仿这种有状态的返回值模式。当然受限于 Java 语言,这种模拟是有限的。
在 PyParsec 里,我也实现了一个 Result 类型,通过 failure + success 的方式,区分状态和返回值:
import json
import logging
SUCCESS = 0
log = logging.getLogger(__name__)
class StateError(Exception):
def __init__(self, error):
super(error)
self.error = error
class Result:
def __init__(self, status, value):
self.logger = log
self.status = status
self.value = value
@classmethod
def success(cls, value):
log.debug(f"create success value of result[{value}]")
return Result(SUCCESS, value)
@classmethod
def failed(cls, error):
log.debug(f"create failure error of [{error}]")
return Result(error, None)
def map(self, func):
if self.isOk():
self.logger.debug(f"call map function with value [{self.value}]")
call = result(func)
return call(self.value)
else:
self.logger.debug(f"ignore map call because result failure [{self.status}]")
return self
def flatMap(self, func):
if self.isOk():
self.logger.debug(f"call flatmap function with value [{self.value}]")
return func(self.value)
else:
self.logger.debug(f"ignore flatmap call because result failure [{self.status}]")
return self
def isOk(self):
return self.status == SUCCESS
def isFailed(self):
return self.status != SUCCESS
def get(self):
if self.isOk():
return self.value
else:
raise StateError(self.status)
def json(self):
"""
生成 json 对象,要求 value 本身是可以被 json 库处理的数据结构,或者提供了 json 方法。
:return:生成的 json 字符串。包含状态和数据。
"""
if self.isOk():
status = "ok"
else:
status = self.status
if hasattr(self.value, "json"):
data = self.value.json()
else:
data = self.value
return json.dumps({
"state": status,
"result": data
})
def success(result):
return Result(SUCCESS, result)
def failed(error):
return Result(error, None)
def result(func):
def call(*args, **argv):
try:
value = func(*args, **argv)
return success(value)
except Exception as error:
return failed(error)
return call
def either(func):
def call(*args, **argv):
try:
error, value = func(*args, **argv)
status = SUCCESS if error is None else error
return Result(status, value)
except Exception as error:
return failed(error)
return call
整个 result.py 中,除了 Result 类型,还包括构造函数 success 和 failed,包括 SUCCESS 常量和一些必要的工具方法。例如日志和 json 支持。
这是 PyParsec 项目的一次重要尝试,但是它还没有成熟,还有有些问题有待实践验证。