F# 从Discriminated Union中提取指定分支类型值的最简洁写法
F# 可辨识联合分支提取简化方案
你当前的用法不属于反模式,这是可辨识联合作为通用承载类型时非常常见的使用场景。针对批量简化提取函数的需求,有以下几种成熟方案:
方案1:通用高阶函数封装(最推荐)
首先实现一个通用的提取逻辑高阶函数,所有分支的提取函数都可以通过一行代码生成,同时统一错误信息格式:
// 通用提取逻辑,入参依次为分支名、分支匹配函数、输入DU实例 let unwrap caseName matcher input = match matcher input with | Some value -> value | None -> invalidOp $"Expecting {caseName}"
针对你的StateEvent类型,所有提取函数可以批量写成如下形式:
let checkPromptUpdated = unwrap nameof<PromptUpdated> (function | PromptUpdated p -> Some p | _ -> None) let checkCorrectAnswerUpdated = unwrap nameof<CorrectAnswerUpdated> (function | CorrectAnswerUpdated v -> Some v | _ -> None) let checkDifficultyUpdated = unwrap nameof<DifficultyUpdated> (function | DifficultyUpdated v -> Some v | _ -> None) let checkTagsUpdated = unwrap nameof<TagsUpdated> (function | TagsUpdated v -> Some v | _ -> None) let checkNotesUpdated = unwrap nameof<NotesUpdated> (function | NotesUpdated v -> Some v | _ -> None) let checkAuthorUpdated = unwrap nameof<AuthorUpdated> (function | AuthorUpdated v -> Some v | _ -> None)
这种写法不需要每个函数都手写异常逻辑,错误信息自动匹配对应的分支名,维护成本极低。
方案2:极简模式匹配(适用于输入分支可100%确定的场景)
如果你能保证所有调用提取函数的上下文,输入的DU实例一定是对应分支,可以直接关闭当前文件的不完整匹配警告,用你最开始的最简写法:
// 在文件最顶部添加,关闭不完整模式匹配警告 #nowarn "0025" let checkPromptUpdated (PromptUpdated prompt) = prompt let checkCorrectAnswerUpdated (CorrectAnswerUpdated answer) = answer // 其余分支以此类推
这种写法代码量最小,运行性能也最高,只要上下文的输入分支正确性有保障,完全可以放心使用。
方案3:安全Option返回(适用于输入分支不确定的场景)
如果不能保证输入一定是对应分支,建议将返回值改为Option<'T>强制调用方处理异常情况,避免运行时抛出异常:
let tryUnwrap matcher input = matcher input // 提取函数示例,返回Option<对应分支类型> let tryGetPromptUpdated = tryUnwrap (function | PromptUpdated p -> Some p | _ -> None) let tryGetCorrectAnswerUpdated = tryUnwrap (function | CorrectAnswerUpdated v -> Some v | _ -> None) // 其余分支以此类推
内容的提问来源于stack exchange,提问作者SteelCityRKP
相关产品推荐
相关产品推荐

