Skip to content

Commit

Permalink
Add Expected.handle
Browse files Browse the repository at this point in the history
  • Loading branch information
evhub committed Oct 31, 2023
1 parent 959ce34 commit 0c7a5af
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 1 deletion.
6 changes: 6 additions & 0 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,10 @@ data Expected[T](result: T? = None, error: BaseException? = None):
if not self:
raise self.error
return self.result
def handle(self, err_type, handler: BaseException -> T) -> Expected[T]:
if not self and _coconut.isinstance(self.error, err_type):
return self.__class__(handler(self.error))
return self
```

`Expected` is primarily used as the return type for [`safe_call`](#safe_call). Generally, the best way to use `Expected` is with [`fmap`](#fmap), which will apply a function to the result if it exists, or otherwise retain the error. If you want to sequence multiple `Expected`-returning operations, `.and_then` should be used instead of `fmap`.
Expand Down Expand Up @@ -3336,6 +3340,8 @@ def safe_call(f, /, *args, **kwargs):
return Expected(error=err)
```

To define a function that always returns an `Expected` rather than raising any errors, simply decorate it with `@safe_call$`.

##### Example

**Coconut:**
Expand Down
7 changes: 7 additions & 0 deletions __coconut__/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ class Expected(_BaseExpected[_T]):
if not self:
raise self.error
return self.result
def handle(self, err_type, handler: BaseException -> T) -> Expected[T]:
if not self and _coconut.isinstance(self.error, err_type):
return self.__class__(handler(self.error))
return self
'''
__slots__ = ()
_coconut_is_data = True
Expand Down Expand Up @@ -416,6 +420,9 @@ class Expected(_BaseExpected[_T]):
def unwrap(self) -> _T:
"""Unwrap the result or raise the error."""
...
def handle(self, err_type: _t.Type[BaseException], handler: _t.Callable[[BaseException], _T]) -> Expected[_T]:
"""Recover from the given err_type by calling handler on the error to determine the result."""
...

_coconut_Expected = Expected

Expand Down
11 changes: 10 additions & 1 deletion coconut/compiler/templates/header.py_template
Original file line number Diff line number Diff line change
Expand Up @@ -1741,6 +1741,10 @@ class Expected(_coconut.collections.namedtuple("Expected", ("result", "error")){
if not self:
raise self.error
return self.result
def handle(self, err_type, handler: BaseException -> T) -> Expected[T]:
if not self and _coconut.isinstance(self.error, err_type):
return self.__class__(handler(self.error))
return self
'''
__slots__ = ()
{is_data_var} = True
Expand Down Expand Up @@ -1803,6 +1807,11 @@ class Expected(_coconut.collections.namedtuple("Expected", ("result", "error")){
if not self:
raise self.error
return self.result
def handle(self, err_type, handler):
"""Recover from the given err_type by calling handler on the error to determine the result."""
if not self and _coconut.isinstance(self.error, err_type):
return self.__class__(handler(self.error))
return self
class flip(_coconut_base_callable):
"""Given a function, return a new function with inverse argument order.
If nargs is passed, only the first nargs arguments are reversed."""
Expand Down Expand Up @@ -1912,7 +1921,7 @@ def mapreduce(key_value_func, iterable, **kwargs):
old_val = collection.get(key, _coconut_sentinel)
if old_val is not _coconut_sentinel:
if reduce_func is False:
raise ValueError("duplicate key " + repr(key) + " with reduce_func=False")
raise ValueError("mapreduce()/collectby() got duplicate key " + repr(key) + " with reduce_func=False")
val = reduce_func(old_val, val)
collection[key] = val
return collection
Expand Down
2 changes: 2 additions & 0 deletions coconut/tests/src/cocotest/agnostic/suite.coco
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,8 @@ forward 2""") == 900
haslocobj = hasloc([[1, 2]])
haslocobj |>= .iloc$[0]$[1]
assert haslocobj == 2
assert safe_raise_exc().error `isinstance` Exception
assert safe_raise_exc().handle(Exception, const 10).result == 10

# must come at end
assert fibs_calls[0] == 1
Expand Down
4 changes: 4 additions & 0 deletions coconut/tests/src/cocotest/agnostic/util.coco
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,10 @@ def minus(a, b) = b - a
def raise_exc():
raise Exception("raise_exc")

@safe_call$
def safe_raise_exc() =
raise_exc()

def does_raise_exc(func):
try:
return func()
Expand Down

0 comments on commit 0c7a5af

Please sign in to comment.