What are the performance benefits of using ~ (lazy pattern matching) in Data.List’s partition. Contrived examples of lazy pattern matching suggest that it is useful when the values inside the tuple constructor are never used (f (x,y) = 1). In partition (select, below), the lists ts, fs are always used (if the predicate p applied to x is True, or not). I’m sure this is a very well informed decision to use ~, but what is the point? Why not strict pattern matching?
partition :: (a -> Bool) -> [a] -> ([a],[a])
{-# INLINE partition #-}
partition p xs = foldr (select p) ([],[]) xs
select :: (a -> Bool) -> a -> ([a], [a]) -> ([a], [a])
select p x ~(ts,fs) | p x = (x:ts,fs)
| otherwise = (ts, x:fs)
(Note: I’ve already looked here! it doesn’t answer the above question)
The point is that with strict pattern matching, assembling of the result could only start when the end of the list to be partitioned has been reached, in particular,
partitionwould not work at all for infinite lists.With a strict pattern match, the argument must be evaluated to WHNF. That means the entire
foldrof the tail must have finished before it is decided whetherxis put in the first or the second component.With a lazy pattern match, a pair is constructed immediately, with
xas the first element of the appropriate component, and the rest of the two lists to be evaluated later, when needed.