Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Result merge method #225

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions expression/core/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
error-handling, which is often referred to as Railway-oriented
Programming.

There is also a simplifyed alias of this type called `Try` that pins
There is also a simplified alias of this type called `Try` that pins
the Result type to Exception.
"""

Expand Down Expand Up @@ -144,19 +144,11 @@ def bind(self, mapper: Callable[[_TSource], Result[_TResult, _TError]]) -> Resul

def is_error(self) -> bool:
"""Returns `True` if the result is an `Error` value."""
match self:
case Result(tag="ok"):
return False
case _:
return True
return self.tag == "error"

def is_ok(self) -> bool:
"""Return `True` if the result is an `Ok` value."""
match self:
case Result(tag="ok"):
return True
case _:
return False
return self.tag == "ok"

def dict(self) -> builtins.dict[str, _TSource | _TError | Literal["ok", "error"]]:
"""Return a json serializable representation of the result."""
Expand Down Expand Up @@ -188,6 +180,13 @@ def or_else_with(self, other: Callable[[_TError], Result[_TSource, _TError]]) ->
"""Return the result if it is Ok, otherwise return the result of the other function."""
return self if self.is_ok() else other(self.error)

def merge(self: Result[_TSource, _TSource]) -> _TSource:
"""Merge the ok and error values into a single value.

This method is only available on Results where _TSource and _TError are the same type.
"""
return self.default_with(lambda x: x)

def to_option(self) -> Option[_TSource]:
"""Convert result to an option."""
match self:
Expand Down
31 changes: 31 additions & 0 deletions tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import Callable, Generator
from dataclasses import dataclass
from typing import Any, Annotated

import pytest
Expand Down Expand Up @@ -524,6 +525,7 @@ def test_result_swap_with_error():
xs = result.swap(error)
assert xs == Ok(1)


def test_ok_or_else_ok():
xs: Result[int, str] = Ok(42)
ys = xs.or_else(Ok(0))
Expand Down Expand Up @@ -570,3 +572,32 @@ def test_error_or_else_with_error():
xs: Result[str, str] = Error("original error")
ys = xs.or_else_with(lambda error: Error(f"new error from {error}"))
assert ys == Error("new error from original error")


def test_merge_ok():
assert Result.Ok(42).merge() == 42


def test_merge_error():
# Explicit type annotation required as merge favours the _TSource type
xs: Result[str, str] = Error("error")
assert xs.merge() == "error"


class Parent:
pass


@dataclass
class Child1(Parent):
x: int


@dataclass
class Child2(Parent):
pass


def test_merge_subclasses():
xs: Result[Parent, Parent] = Result.Ok(Child1(x=42))
assert xs.merge() == Child1(x=42)
Loading