Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Pattern Matching: Readability and Performance

I'm going through the learn you a haskell tutorial and I've been tripping over some of the examples the author has given.

For example he reimplemented zip as follows:

zip' :: [a] -> [b] -> [(a,b)]  
zip' _ [] = []  
zip' [] _ = []  
zip' (x:xs) (y:ys) = (x,y):zip' xs ys

He uses a similar approach for all his other examples, where he puts the most specific patterns first. Here is a slightly different version of the zip function:

zip' :: [a] -> [b] -> [(a,b)]
zip' (x:xs) (y:ys)  = (x, y):zip' xs ys
zip' _ _            = []

As far as I understand both methods do the same thing. If an empty list is provided either way (x:xs) or (y:ys) won't match which will finish the recursion by appending the empty list [].

  1. I personally prefer the second version for readability, but maybe I'm wrong in doing so.
  2. Does it have any effect on the performance of a method? As far as I understand if the top most pattern does not match, Haskell will check against the next pattern. Does the order of the patterns affect performance?

Kind regards,

Edit:

Possibly duplicate of: Haskell GHC: what is the time complexity of a pattern match with N constructors?

Summary: The order of the patterns is very important for the semantics (in terms of strict evaluation of the arguments) and the readability of a function. The pattern match itself will always be in O(1) time complexity.

like image 971
Nima Mousavi Avatar asked Feb 06 '16 18:02

Nima Mousavi


People also ask

What is pattern matching in Haskell?

In Haskell pattern matching does the same thing, which they or attempt to match any given value or user passed value to the function, after this we can return any value we want or simply we can assign the value to the variable and return it.

What is a lazy pattern in Haskell?

4.4 Lazy Patterns There is one other kind of pattern allowed in Haskell. It is called a lazy pattern, and has the form ~pat. Lazy patterns are irrefutable: matching a value v against ~pat always succeeds, regardless of pat.

What is Haskell's case expression?

Haskell's case expressionprovides a way to solve this problem. Indeed, the meaning of pattern matching in function definitions is specified in the Report in terms of case expressions, which are considered more primitive. In particular, a function definition of the form: fp11... p1k=e1 fpn1... pnk=en

What happens if we do not provide catch all block in Haskell?

2) catch all block: If we do not provide the catch all block then it will throw as error, because while doing pattern matching it will not able to find anything and throw us error. If you provide the catch block then the error can be avoided also, it is safer to use also known as default pattern matching block in Haskell.


1 Answers

As far as I understand both methods do the same thing.

almost; with an exception:

\> zip' undefined []   -- 1st definition of zip'
[]
\> zip' (filter (< 4) [1..]) [1, 2, 3]
[(1,1),(2,2),(3,3)]

whereas:

\> zip' undefined []   -- 2nd definition of zip'
*** Exception: Prelude.undefined
\> zip' (filter (< 4) [1..]) [1, 2, 3]
[(1,1),(2,2),(3,3)   -- gets stuck here; never returns

in other words, the 2nd definition always forces weak head normal form for both arguments.

Performance-wise, this means that one can construct a pathological example such that WHNF involves heavy computations, therefore one definition performs very differently than the other.

like image 192
behzad.nouri Avatar answered Sep 26 '22 17:09

behzad.nouri