dbrattli/Expression

Bind method doesn't propagate error types correctly in chained operations

Opened this issue · 0 comments

The current implementation of the bind method in the Result class does not properly propagate or merge error types when chaining operations. For example:

@dataclass
class Order:
    amount: float
    product: str

def verify_amount(order: Order) -> Result[Order, ValueError]:
    if order.amount < 0:
        return Error(ValueError("Amount cannot be negative"))
    return Ok(order)

def verify_product(order: Order) -> Result[Order, ValueError]:
    if order.product not in ["Apple", "Banana"]:
        return Error(ValueError("Product not found"))
    return Ok(order)

def order_amount_with_commission(order: Order) -> Result[float, ValueError | KeyError]:
    return Ok(order.amount * 1.1)

order = Order(amount=-100, product="Apple1")

result = (
    Ok(order)
    .bind(verify_amount)
    .bind(verify_product)
    .bind(order_amount_with_commission)
)

The expected type of result should be:
Result[float, ValueError | KeyError]

However, the actual inferred type is:
Result[float, Any]

I found that a small change in the bind method's type signature partially solves the issue by propagating and merging error types more accurately. However, Any still remains in the inferred Result type:

def bind(self, mapper: Callable[[_TSourceOut], Result[_TResult, _TError | _TOther]]) -> Result[_TResult, _TError | _TOther]:

Now type inferred as Result[float, ValueError | KeyError | Any]