F#中嵌套Result类型扁平化及DTO转Domain的Result封装问题
问题1:将toDomain重构为返回Result类型的F#标准写法
抛弃抛出异常的写法,让toDomain直接返回Result<Domain.Worker, exn>,通过模式匹配把非法状态值映射到Error分支,完全用函数式方式处理错误,避免副作用:
module DTO type Worker = { // DTO字段,如存储状态的数字类型 Status : int // 其他业务字段... } with static member toDomain (dbo : Worker) : Result<Domain.Worker, exn> = // 单独处理状态转换,得到Result<WorkerStatus, exn> let statusResult = match dbo.Status with | 1 -> Ok Domain.WorkerStatus.Status1 | 2 -> Ok Domain.WorkerStatus.Status2 | 3 -> Ok Domain.WorkerStatus.Status3 | invalidVal -> Error (exn $"数据库存在无效Worker状态值: {invalidVal}") // 用Result.map将状态的Ok结果映射为完整的Domain.Worker statusResult |> Result.map (validStatus -> { Domain.Worker.Status = validStatus // 映射其他字段,如Name = dbo.Name等 })
这种写法完全遵循F#函数式风格,错误被显式包含在Result类型中,不需要依赖异常处理或可变变量。
问题2:扁平化嵌套的Result类型
用Result.bind替代Result.map处理内部的Option和Result嵌套,最终得到目标类型Async<Result<Domain.Worker option, exn>>:
方式1:内联处理逻辑
methodResult |> Async.map (fun originalResult -> originalResult |> Result.bind (fun dtoOption -> match dtoOption with | None -> Ok None // 无DTO时直接返回Ok None | Some dto -> // 调用返回Result的toDomain,再将Ok结果包装为Some DTO.Worker.toDomain dto |> Result.map Some))
方式2:封装辅助函数简化代码
如果需要复用该逻辑,可定义通用辅助函数:
/// 将Option<T>通过返回Result的函数转换为Result<Option<U>, E> let mapOptionToResult f = function | None -> Ok None | Some x -> f x |> Result.map Some
转换代码会更简洁:
methodResult |> Async.map (Result.bind (mapOptionToResult DTO.Worker.toDomain))
处理后,原本嵌套的Result<Result<Domain.Worker, exn> option, exn>会被扁平化为Result<Domain.Worker option, exn>,完全符合预期类型。
内容的提问来源于stack exchange,提问作者Juan
相关产品推荐
相关产品推荐

