首页 > 解决方案 > 有没有一种简单的方法来继承计算表达式的行为?

问题描述

您将如何构建一个新的计算表达式来继承现有的大多数行为,但您可能需要覆盖某些行为?

语境:

从现在开始,我将使用 CE 作为计算表达式的缩写。

我正在使用https://github.com/demystifyfp/FsToolkit.ErrorHandling

我有一个“分层”应用程序,它有一个从数据库层获取的存储库。

到目前为止,数据库层中的所有函数都Task<Result<'T, DatabaseError>>通过taskResult { ... }CE 从FsToolkit.ErrorHandling.

但是在存储库中,我总是想返回Task<Result<'T, RepositoryError>>RepositoryError可以很容易地从中派生DatabaseError,这意味着我的大部分代码看起来像这样:

let getAll con offset chunk = taskResult {
    let! products =
        ProductEntity.allPaginated offset chunk con
        |> TaskResult.mapError RepositoryError.fromDatabaseError
    let! totalCount =
        ProductEntity.countAll con
        |> TaskResult.mapError RepositoryError.fromDatabaseError
    return { Items = products; TotalCount = totalCount }
}

目标:

  1. 我希望TaskResult.mapError RepositoryError.fromDatabaseError在新的 CE 内部进行所有这些调用。让我们称之为repoTaskResult { ... }
  2. 我需要拥有原始taskResult { ... }CE的所有当前功能
let getAll con offset chunk = repoTaskResult {
    let! products = ProductEntity.allPaginated offset chunk con
    let! totalCount = ProductEntity.countAll con
    return { Items = products; TotalCount = totalCount }
}

编辑:

A. 尝试通过继承解决它(但推断正确的类型不起作用)

type RepositoryTaskResultBuilder() =
    inherit TaskResultBuilder()
    member __.Bind(databaseTaskResult: Task<Result<'T, DatabaseError>>, binder) =
        let repoTaskResult = databaseTaskResult |> TaskResult.mapError RepositoryError.fromDatabaseError
        __.Bind(taskResult = repoTaskResult, binder = binder)

let repoTaskResult = RepositoryTaskResultBuilder()

用法:

let getAll con offset chunk = repoTaskResult {
    let! other = Task.singleton 1
    let! products = ProductEntity.allPaginated offset chunk con
    let! totalCount = ProductEntity.countAll con
    return { Items = products; TotalCount = totalCount }
}

继承版本的结论:

目标 #2 很容易实现,other正确推断为 int。如果没有任何类型推断的帮助,目标 #1 是无法实现的。默认让!似乎被推断为底层TaskResultBuilder的更通用的Bind方法之一。这意味着整个返回类型被推断为 Task<Result<'T, DatabaseError>>。

如果您希望通过将 return 语句替换为 来帮助进行类型推断return! Result<_,RepositoryError>.Ok { Items = products; TotalCount = totalCount },那么您就可以开始了,就像现在 let!之前的语句正确地使用了新实现的 Bind 方法。

标签: f#computation-expression

解决方案


推荐阅读