LanguageExt:Linq查询中使用返回Task<Either<string, T>>的方法时编译错误
解决LanguageExt EitherAsync LINQ查询类型不匹配问题
你的编译错误根源在于EitherAsync的LINQ链式绑定要求所有步骤的Left类型必须一致。查看代码可以发现:
Decrypt、CheckIfDeviceExists、CheckIfUserExists返回的Either(或Task)的Left类型是 UnitCreateUser、CreateDevice返回的Either的Left类型是string
当使用LINQ查询(from ... in ...)绑定EitherAsync时,整个monad链的Left类型必须兼容(通常是相同类型)——因为一旦某个步骤返回Left,整个链会短路并传递这个错误值,类型不统一的话编译器无法处理隐式转换,就会抛出CS0029这类错误。
解决方案:统一Left类型
你需要把所有步骤的Left类型统一为string(因为后续步骤的错误信息是字符串,更有业务意义),可以通过MapLeft方法将前几步的Unit类型错误转换为对应的错误字符串:
修正后的完整代码
static Either<Unit, (string serialNumber, string password)> Decrypt(string data) => // 解密输入字符串并提取序列号和密码 ("serial", "password"); static async Task<Either<Unit, Unit>> CheckIfDeviceExists(string serialNumber) => // 实际会检查数据库,此处简化 serialNumber.Contains("x") ? Right(unit) : Left(unit); static async Task<Either<Unit, Unit>> CheckIfUserExists(string serialNumber) => // 实际会检查数据库,此处简化 serialNumber.Contains("x") ? Right(unit) : Left(unit); static async Task<Either<string, User>> CreateUser(string serialNumber, string password) => // 创建ASP.NET Core Identity用户并返回,或返回错误信息字符串 new User(); static async Task<Either<string, Unit>> CreateDevice(User user, string serialNumber) => // 创建新设备并保存到数据库,Left为错误信息 unit; // 模拟模型 record User(); static async Task RegisterDevice(string data) { var result = await ( // 将Decrypt的Left从Unit转为错误字符串 from d in Decrypt(data).ToAsync().MapLeft(_ => "解密失败") // 将设备检查的Left转为错误字符串 from _1 in CheckIfDeviceExists(d.serialNumber).ToAsync().MapLeft(_ => "设备已存在") // 将用户检查的Left转为错误字符串 from _2 in CheckIfUserExists(d.serialNumber).ToAsync().MapLeft(_ => "用户已存在") from user in CreateUser(d.serialNumber, d.password).ToAsync() from _3 in CreateDevice(user, d.serialNumber).ToAsync() select unit ); }
为什么之前的尝试无效?
你尝试用RightAsync<string, User>(CreateUser(...))是错误的:
RightAsync接受的参数是Task<T>或T,但CreateUser返回的是Task<Either<string, User>>,类型不匹配- 核心问题不是
ToAsync()的转换,而是Left类型不统一,即使你强行转换这一步,前面的类型不匹配问题依然存在
替代方案:调整方法返回类型
如果你不想在LINQ查询中做MapLeft转换,也可以直接修改CheckIfDeviceExists和CheckIfUserExists的返回类型,让它们的Left直接是string:
static async Task<Either<string, Unit>> CheckIfDeviceExists(string serialNumber) => serialNumber.Contains("x") ? Right(unit) : Left("设备已存在"); static async Task<Either<string, Unit>> CheckIfUserExists(string serialNumber) => serialNumber.Contains("x") ? Right(unit) : Left("用户已存在");
这样后续的LINQ查询不需要额外转换,直接链式调用即可。
内容的提问来源于stack exchange,提问作者Avrohom Yisroel
相关产品推荐
相关产品推荐

