C# FP:使用错误处理功能方式进行验证和执行 - space 进行改进?

C# FP: Validation and execution with error handling functional way - space for improvement?

我不熟悉 C# 中的函数式思维方式(好吧...不限于语言)。假设有方法:

T LoadRecord<T>(int id)

概念

1。验证

当给出无效输入时,我应该return类似Either<IEnumerable<ValidationError>, T>

2。执行

当调用可能抛出的 DB/API/... 时,我应该 return Either<Exception, T>.

3。一些或none记录

因为条目可能存在也可能不存在我正在 returning Option<T>

最终签名

如果结合以上所有内容,您将得到以下结果:

Either<IEnumerable<ValidationError>, Either<Exception, Option<T>> LoadRecord<T>(int id)

我可以介绍类型如:

现在,我的方法签名如下所示:

Validation<Result<Option<T>>> LoadRecord<T>(int id)

用法

return LoadRecord<User>(1).Match(
           vl => BadRequest(),
           vr => vr.Match(
                     el => StatusCode(500),
                     er => er.Some(u => Ok(u))
                             .None(NotFound());

强制执行

try
{
    var u = LoadRecord<User>(id);
    if (u == null)
    {
        return NotFound();
    }

    return Ok(u);
}
catch (ValidationException)
{
    return BadRequest();
}
catch (Exception)
{
    return StatusCode(500);
}

问题

如您所见,方法的签名仍然很奇怪 - 乍一看它给了您 "validation",但我真的要求 T。用法也不是很漂亮,但确实比命令式更简洁。

这是正确的做法吗?有没有办法改进 signature/readability 的代码?

  1. 嗯,是的。根据所涉及的复杂性,现代(我猜是 C# 7)元组可能就足够了:(bool failed, T result)。限制是没有负责正确链接的方法 - fmaping 在 Haskell 方面 - 你的容器(更好地说,但理论上不完全正确 - 你的 Functors 和,不太常见,你的 Monads).
  2. Either<IEnumerable<ValidationError>, Either<Exception, Option<T>> LoadRecord<T>(int id) - 不。观察您的签名:至于 TFail,您要么得到 IEnumerable<ValidationError> 要么 Exception;所以,从技术上讲,你需要一个 Exception | IEnumerable<ValidationError> 类型,认为在 C# 中实现需要相当多的样板代码。同样适用于 Option<T> 部分,它代表一个成功的结果:你不需要 IOption 被 returned;即使它在那里,您的 实现 也负责解包成功的结果并输出 T,或者通过 Either 报告 TFail 错误机制。结论:是的,容器很棒,但要避免无休止的嵌套:有时间包装东西,也有时间打开它们:感受这一刻
  3. 最后但并非最不重要的一点:异常处理应该委托给容器本身。 Rx 是一个很好的例子:它要求您提供 onError: Action<Exception> lambda,它在捕获异常时被调用。因此,您可以让您的客户自定义所需的行为,而不是一次硬编码。不仅如此,由于您的特定示例是特定的,IOption 是一种处理错误的方法,因此在您的特定情况下 - 但不是一般规则 - 否 onError 需要:只需吞下一个异常和 return Nothing.