Ложное совпадение шаблонов в Data.List

Каковы преимущества производительности использования ~ (ленивое совпадение шаблонов) в разделе Data.List. Продуманные примеры ленивого сопоставления образцов предполагают, что это полезно, когда значения внутри конструктора кортежа никогда не используются (f (x, y) = 1). В разделе (выберите, ниже) всегда используются списки ts, fs (если предикат p, примененный к x, является True или нет). Я уверен, что это очень хорошо информированное решение использовать ~, но в чем смысл? Почему не строгое соответствие шаблонов?

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)

(Примечание: я уже смотрел здесь! он не отвечает на вышеуказанный вопрос)

Ответы

Ответ 1

Дело в том, что при строгом сопоставлении шаблонов сбор результатов мог начаться только тогда, когда был достигнут конец разбиваемого списка, в частности, partition не работал бы вообще для бесконечных списков.

При строгом совпадении шаблона аргумент должен быть оценен в WHNF. Это означает, что весь foldr хвоста должен быть закончен, прежде чем будет принято решение о том, помещается ли x в первый или второй компонент.

select p x (foldr (select p) ([],[]) (y:z:ws))
~> select p x (select p y (select p z (foldr (select p) ([],[]) ws)))

С ленивым совпадением шаблонов пара создается немедленно, а x - как первый элемент соответствующего компонента, а остальные два списка будут оцениваться позже, когда это необходимо.