İşlevsel programlamada birinin istisnalar atmaması ve / veya gözetmemesi gerektiği söylendi. Bunun yerine hatalı bir hesaplama en düşük değer olarak değerlendirilmelidir. Python'da (veya işlevsel programlamayı tam olarak desteklemeyen diğer dillerde ), "saf kalmaya" ters giden bir şey yanlış giderse, ancak tanımlamaya kesinlikle uymuyorsa ) bir kişi geri dönebilir None
(veya en düşük değer olarak kabul edilen başka bir alternatif None
) öyleyse, kişi ilk başta bir hata gözlemlemek zorundadır, yani
def fn(*args):
try:
... do something
except SomeException:
return None
Bu saflığı ihlal ediyor mu? Ve eğer öyleyse, bu, yalnızca Python'daki hataları ele almanın imkansız olduğu anlamına mı geliyor?
Güncelleştirme
Eric Lippert, yorumunda, AP'de istisnaları tedavi etmenin başka bir yolunu hatırlattı. Bunu Python'da pratikte hiç görmedim, bir yıl önce FP okuduğumda onunla oynadım. Burada, herhangi bir optional
dekore edilmiş işlev Optional
, normal çıktılar için ve ayrıca bir istisnalar listesi için boş olabilecek değerleri döndürür (belirtilmemiş istisnalar, yürütmeyi sona erdirebilir). Carry
Her bir adımın (gecikmeli fonksiyon çağrısı) Optional
önceki adımdan boş bir çıktı aldığı ve basitçe üzerinden geçtiği veya başka bir şekilde kendisini yenisini değerlendirdiği gecikmeli bir değerlendirme yaratır Optional
. Sonunda son değer ya normaldir ya da Empty
. Burada try/except
blok bir dekoratörün arkasına gizlenmiştir, bu nedenle belirtilen istisnalar iade tipi imzanın bir parçası olarak kabul edilebilir.
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