Haskell 如何上报命令行参数校验错误并返回正确退出码
类型错误根源
类型检查不通过的核心原因是case表达式两个分支返回类型不统一:
Left分支调用的printErrorsThenExit被定义为返回IO ()Right分支return options返回类型为IO Options
Haskell要求同个case表达式的所有分支必须返回相同类型的值,因此无法编译。
实际上exitWith是永远不会正常返回的函数(执行后直接终止进程),其原生类型为ExitCode -> IO a,支持多态返回值,可以适配任意IO类型,不需要将工具函数的返回值固定为IO ()。
修正步骤
- 首先调整错误退出工具函数的类型签名,利用
exitWith的多态特性:
printErrorsThenExit :: [String] -> ExitCode -> IO a printErrorsThenExit errs code = do -- 建议用unlines拼接错误,每条错误单独占一行,比concat可读性更好 hPutStr stderr $ unlines errs exitWith code
修改后这个函数可以在需要任意IO类型的上下文里使用,因为程序走到exitWith就直接退出,不会产生实际返回值。
- 此时原有的case逻辑不需要额外改动就能通过类型检查,两个分支会自动统一为
IO Options类型:
opts <- case result of Left errors -> printErrorsThenExit errors illegalArgument Right options -> return options
更简洁的写法
可以直接用Either类型自带的either消除器代替手写case,逻辑完全等价,代码更精简:
opts <- either (`printErrorsThenExit` illegalArgument) pure result
可选优化点
当前实现的evaluateActions收集错误时是把新错误插到错误列表头部,最终错误顺序和参数解析顺序是反向的,如果对错误展示顺序有要求,可以在打印前对错误列表做reverse处理,或者调整折叠时的错误拼接顺序。
内容的提问来源于stack exchange,提问作者VilePoison
相关产品推荐
相关产品推荐

