能否用返回Result的同名函数遮蔽F#抛出异常的checkAddressExists?
问题解答
1. 能否通过遮蔽(shadowing)让Result版本服务使用原同名?
完全可以。F#支持变量遮蔽特性,你可以在原抛出异常的checkAddressExists定义之后,重新定义同名函数,用适配器包装原服务实现,后续代码中引用checkAddressExists时,就会自动使用返回Result的新版本。
具体实现方式如下:
// 原抛出异常的服务实现 let checkAddressExists : CheckAddressExists = fun unvalidatedAddress -> dotDotDot() // 通过遮蔽替换为返回Result的版本 let checkAddressExists = serviceExceptionAdapter serviceInfo checkAddressExists
这里要注意:
- 新定义右侧的
checkAddressExists会引用遮蔽生效前的原函数,所以能正确包装原异常抛出逻辑。 - 遮蔽后函数类型会从
CheckAddressExists(UnvalidatedAddress -> CheckedAddress)变为UnvalidatedAddress -> Result<CheckedAddress, RemoteServiceError>,如果有代码依赖原类型注解,需要同步调整。 - 遮蔽仅在当前作用域及子作用域生效,遮蔽前的代码仍会使用原异常抛出版本。
2. 关于CheckAddressExists函数类型的解释
CheckAddressExists是一个函数类型别名,定义为UnvalidatedAddress -> CheckedAddress,核心作用是明确地址检查服务的输入输出契约:
- 输入类型
UnvalidatedAddress:你当前用Undefined(即exn类型)作为临时占位,实际项目中应该替换为包含真实地址信息的自定义类型(比如记录type UnvalidatedAddress = { Street: string; City: string; PostalCode: string })。 - 输出类型
CheckedAddress:这是一个单例判别联合(DU),通过包裹UnvalidatedAddress来标记该地址已通过存在性验证,属于领域驱动设计里的“已验证值对象”模式,用类型区分未验证/已验证数据,避免错误使用。
这个类型别名的价值在于:
- 让代码意图更直观,看到
CheckAddressExists就能明确这是地址检查服务的标准签名。 - 降低替换实现的成本,后续如果有其他地址检查服务,只要符合该签名就能无缝替换。
补充:修正原代码的语法错误
原代码中serviceExceptionAdapter的with分支箭头写错了(用了-),正确写法应该是->,否则会编译失败:
let serviceExceptionAdapter serviceInfo serviceFn x = try Ok (serviceFn x) with | :? TimeoutException as ex -> Error { Service = serviceInfo; Exception = ex } | :? AuthorizationException as ex -> Error { Service = serviceInfo; Exception = ex }
内容的提问来源于stack exchange,提问作者Alexander K.
相关产品推荐
相关产品推荐

