在功能编程中,将不完整的模式匹配视为不好的做法

Dar*_*rio 32 f# haskell functional-programming

在Haskell或F#等函数式语言中使用非详尽的模式机制通常被认为是一种不好的做法,这意味着指定的案例并未涵盖所有可能的输入案例?

特别是,我应该允许代码失败MatchFailureException等,还是应该总是覆盖所有情况并在必要时明确抛出错误?

例:

let head (x::xs) = x
Run Code Online (Sandbox Code Playgroud)

要么

let head list = 
    match list with
    | x::xs -> x
    |    _  -> failwith "Applying head to an empty list"
Run Code Online (Sandbox Code Playgroud)

F#(与Haskell不同)给出了第一个代码的警告,因为[]-case没有被覆盖,但为了简洁起见,我可以忽略它而不破坏功能样式约定吗?MatchFailure确实很好地说明了问题......

Pas*_*uoq 45

如果你使用构造函数完成模式匹配[]而不是catch-all _,编译器将有机会告诉你再次查看函数,并在有人向列表添加第三个构造函数时发出警告.

我的同事和我在一个大型OCaml项目(200,000多行)上工作,强迫我们避免部分模式匹配警告(即使这意味着| ... -> assert false不时写入)并避免所谓的" 脆弱模式匹配 "(模式匹配以这样的方式编写,即也可能无法检测到构造函数的添加.我们认为可维护性有益.


for*_*ran 9

显式优于隐式(借用Python的Zen;))

它与C开关完全相同enum...最好编写所有的情况(通过掉落)而不是只是放一个default,因为编译器会告诉你是否在枚举中添加了新的元素而你忘记了处理它们.


kvb*_*kvb 5

我认为这在很大程度上取决于背景.您是否正在尝试编写健壮,易于调试的代码,或者您是否正在尝试编写简单而简洁的内容?

如果我正在与多个开发人员一起开展长期项目,我会在断言中提供更有用的错误消息.我也同意Pascal的评论,从软件工程的角度来看,不使用通配符是理想的.

如果我正在开发一个我是唯一一个开发人员的小规模项目,我不会再考虑使用不完整的匹配.如有必要,您始终可以检查编译器警告.

我认为这也取决于你所匹配的类型.实际上,没有额外的联合案例会添加到列表类型中,因此您不必担心脆弱的匹配.另一方面,在您控制并正在积极处理的代码中,可能存在不断变化的类型并且添加了额外的联合情况,这意味着防止脆弱匹配可能是值得的.


Pau*_*son 5

这是一个更普遍的问题的特例,"你应该创建部分函数吗".不完整模式匹配仅是部分函数的一个示例.

通常,总功能是优选的.当你发现自己正在寻找一个只需要部分的功能时,问问自己是否可以先在类型系统中解决问题.有时这比它的价值更麻烦(例如,创建一个已知长度的整个类型的列表,只是为了避免"head []"问题).所以这是一个权衡.

或者也许你只是问一下它在部分函数中的优良实践是否可以说出类似的东西

head [] = error "head: empty list"
Run Code Online (Sandbox Code Playgroud)

在这种情况下答案是肯定的!