함수형 프로그래밍에서 예외를 던지거나 관찰해서는 안된다는 말을 들었습니다. 대신 잘못된 계산은 최저값으로 평가해야합니다. 파이썬 (또는 함수형 프로그래밍을 완전히 장려하지 않는 다른 언어) 에서는 무언가가 "순수하게 유지"되는 것이 잘못 될 때마다 리턴 할 수 있습니다 None(또는 None정의를 엄격하게 준수하지는 않지만 다른 값을 최저값으로 취급 함 ). 먼저 오류를 관찰해야합니다. 즉
def fn(*args):
try:
... do something
except SomeException:
return None
이것이 순결을 위반합니까? 그렇다면 파이썬에서 순수하게 오류를 처리하는 것이 불가능하다는 것을 의미합니까?
최신 정보
그의 의견에서 Eric Lippert는 FP에서 예외를 처리하는 또 다른 방법을 상기시켰다. 실제로 파이썬에서 한 일을 본 적이 없지만 1 년 전에 FP를 공부했을 때 나는 그것을 가지고 놀았습니다. 여기서 optional-decorated 함수는 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