有人告诉我,在函数式编程中,不应抛出和/或观察异常。取而代之的是,应将错误的计算结果视为底值。在Python(或其他不完全鼓励函数式编程的语言)中,只要出现“保持纯净”的错误,就可以返回None
(或另一种替代方法,尽管None
它严格地不符合定义)。所以必须首先观察到错误,即
def fn(*args):
try:
... do something
except SomeException:
return None
这违反纯洁吗?如果是这样,是否意味着不可能仅用Python处理错误?
更新资料
埃里克·利珀特(Eric Lippert)在他的评论中使我想起了FP中处理异常的另一种方法。尽管我从未在实践中看到过用Python完成的工作,但一年前我学习FP时我还是玩过它。在这里,任何optional
装饰函数返回的Optional
值对于正常输出以及指定的异常列表都可以为空(未指定的异常仍可以终止执行)。Carry
创建一个延迟的评估,其中每个步骤(延迟的函数调用)要么Optional
从上一步获取非空输出,然后继续传递,要么通过一个new评估自身Optional
。最后,最终值是normal或Empty
。此处,该try/except
块隐藏在装饰器后面,因此可以将指定的异常视为返回类型签名的一部分。
class Empty:
def __repr__(self):
return "Empty"
class Optional:
def __init__(self, value=Empty):
self._value = value
@property
def value(self):
return Empty if self.isempty else self._value
@property
def isempty(self):
return isinstance(self._value, BaseException) or self._value is Empty
def __bool__(self):
raise TypeError("Optional has no boolean value")
def optional(*exception_types):
def build_wrapper(func):
def wrapper(*args, **kwargs):
try:
return Optional(func(*args, **kwargs))
except exception_types as e:
return Optional(e)
wrapper.__isoptional__ = True
return wrapper
return build_wrapper
class Carry:
"""
>>> from functools import partial
>>> @optional(ArithmeticError)
... def rdiv(a, b):
... return b // a
>>> (Carry() >> (rdiv, 0) >> (rdiv, 0) >> partial(rdiv, 1))(1)
1
>>> (Carry() >> (rdiv, 0) >> (rdiv, 1))(1)
1
>>> (Carry() >> rdiv >> rdiv)(0, 1) is Empty
True
"""
def __init__(self, steps=None):
self._steps = tuple(steps) if steps is not None else ()
def _add_step(self, step):
fn, *step_args = step if isinstance(step, Sequence) else (step, )
return type(self)(steps=self._steps + ((fn, step_args), ))
def __rshift__(self, step) -> "Carry":
return self._add_step(step)
def _evaluate(self, *args) -> Optional:
def caller(carried: Optional, step):
fn, step_args = step
return fn(*(*step_args, *args)) if carried.isempty else carried
return reduce(caller, self._steps, Optional())
def __call__(self, *args):
return self._evaluate(*args).value