In Functional Programming, is it considered a bad practice to have incomplete pattern matchings

前端 未结 7 1348
梦毁少年i
梦毁少年i 2020-12-09 02:12

Is it generally considered a bad practice to use non-exhaustive pattern machings in functional languages like Haskell or F#, which means that the cases specified don\'t cove

相关标签:
7条回答
  • 2020-12-09 02:22

    This is a special case of a more general question, which is "should you ever create partial functions". Incomplete pattern matches are only one example of partial functions.

    As a rule total functions are preferable. When you find yourself looking at a function that just has to be partial, ask yourself if you can solve the problem in the type system first. Sometimes that is more trouble than its worth (e.g. creating a whole type of lists with known lengths just to avoid the "head []" problem). So its a trade-off.

    Or maybe you just asking whether its good practice in partial functions to say things like

    head [] = error "head: empty list"
    

    In which case the answer is YES!

    0 讨论(0)
  • 2020-12-09 02:29

    If you complete your pattern-matchings with a constructor [] and not the catch-all _, the compiler will have a chance to tell you to look again at the function with a warning the day someone adds a third constructor to lists.

    My colleagues and I, working on a large OCaml project (200,000+ lines), force ourselves to avoid partial pattern-matching warnings (even if that means writing | ... -> assert false from time to time) and to avoid so-called "fragile pattern-matchings" (pattern matchings written in such a way that the addition of a constructor may not be detected) too. We consider that the maintainability benefits.

    0 讨论(0)
  • 2020-12-09 02:31

    Non-exhaustive pattern machings are idiomatic in Haskell but extremely bad style in F# (and OCaml, Standard ML etc.).

    Exhaustiveness checking is a very valuable way to catch errors at compile time in strict languages like F# but lazy languages like Haskell often use infinite lazy lists where the empty list cannot arise so they (historically) chose brevity over statically-checked correctness.

    0 讨论(0)
  • 2020-12-09 02:32

    Explicit is better than implicit (borrowed from the Zen of Python ;))

    It's exactly the same as in a C switch over an enum... It's better to write all the cases (with a fall through) rather than just putting a default, because the compiler will tell you if you add new elements to the enumeration and you forgot to handle them.

    0 讨论(0)
  • 2020-12-09 02:32

    This question has two aspects.

    1. For the user of the API, failwith... simply throws a System.Exception, which is unspecific (and therefore is sometimes considered a bad practice in itself). On the other hand, the implicitly thrown MatchFailureException can be specifically caught using a type test pattern, and therefore is preferrable.
    2. For the reviewer of the implementation code, failwith... clearly documents that the implementer has at least given some thought about the possible cases, and therefore is preferrable.

    As the two aspects contradict each other, the right answer depends on the circumstances (see also kvb's answer). A solution which is 100% "correct" from any point of view would have to

    • deal with every case explicitly,
    • throw a specific exception where necessary, and
    • clearly document the exception

    Example:

    /// <summary>Gets the first element of the list.</summary>
    /// <exception cref="ArgumentException">The list is empty.</exception>
    let head list = 
        match list with
        | [] -> invalidArg "list" "The list is empty." 
        | x::xs -> x
    
    0 讨论(0)
  • 2020-12-09 02:35

    The Haskell prelude (standard functions) contains many partial functions, e.g. head and tail only work on non-empty lists, but don't ask me why.

    0 讨论(0)
提交回复
热议问题