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

Zde*_*něk 6 c# functional-programming optional either

我是C#中功能性思维方式的新手(嗯......不仅限于语言).让我们说有方法:

T LoadRecord<T>(int id)
Run Code Online (Sandbox Code Playgroud)

概念

1.验证

当给出无效输入时,我应该返回类似的内容Either<IEnumerable<ValidationError>, T>.

2.执行

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

3.一些或没有记录

因为入境可能存在,也可能不存在,我正在回归Option<T>.

最终签名

如果你将上述所有内容结合起来,你最终会得到:

Either<IEnumerable<ValidationError>, Either<Exception, Option<T>> LoadRecord<T>(int id)
Run Code Online (Sandbox Code Playgroud)

我可以介绍如下类型:

  • Validation<T>(〜: Either<IEnumerable<ValidationError>, T>)
  • Result<T>(〜: Either<Exception, T>)

现在,我的方法的签名看起来像:

Validation<Result<Option<T>>> LoadRecord<T>(int id)
Run Code Online (Sandbox Code Playgroud)

用法

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

势在必行的实施

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

    return Ok(u);
}
catch (ValidationException)
{
    return BadRequest();
}
catch (Exception)
{
    return StatusCode(500);
}
Run Code Online (Sandbox Code Playgroud)

你可以看到方法的签名仍然很奇怪 - 乍一看它给你"验证",但我真的要求T.用法也不是很漂亮,但确实比命令式更简洁.

这是正确的方法吗?有没有办法如何提高代码的签名/可读性?

Ser*_*bov 1

  1. 嗯,是的。根据所涉及的复杂性,现代(我猜是 C# 7)元组可能就足够了(bool failed, T result):限制是没有方法负责根据fmapHaskell 正确链接你的容器(更好的说法是,但理论上不完全正确 - 你的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返回;即使它在那里,您的实现也负责解开成功的结果并只是吐出,或通过该机制T报告错误。结论是:是的,容器很棒,但要避免无休止的嵌套:有时间包装东西,也有时间打开它们:感受那一刻TFailEither
  3. 最后但同样重要的是:异常处理应该委托给容器本身。Rx是一个很好的例子:它要求您提供onError: Action<Exception>lambda,当捕获异常时会调用该 lambda。因此,您可以让客户自定义所需的行为,而不是一次对其进行硬编码。更重要的是,由于您的特定示例是特定的,IOption这是一种处理错误的方法,因此在您的特定情况下 -但不是一般规则-onError不需要:只需吞下异常并 return Nothing