Você pode corrigir to10plususando uma correspondência irrefutável (ou seja, um ~prefixo) na sua definição de merge:
merge (a, b) ~(as, bs) = (a:as, b+bs)
A razão da diferença de comportamento entre to10e to10plusé que to10pode retornar o primeiro elemento da lista sem precisar avaliar to10 xse, portanto, sem inspecionar xs.
Por outro lado, antes que possa retornar qualquer coisa, to10plusdeve chamar mergecom sucesso com os argumentos (x, 1)e to10plus xs. Para que esta chamada seja bem-sucedida, to10plus xsdeve ser avaliada o suficiente para garantir que ela corresponda ao padrão (as, bs)usado na definição de merge, mas essa avaliação requer a inspeção de elementos de xs, que ainda não estão disponíveis.
Você também pode ter evitado o problema definindo to10plusum pouco diferente:
to10plus (x:xs) | x < 10 = (x:as,1+bs)
| otherwise = ([], 0)
where (as,bs) = to10plus xs
Aqui, to10pluspode fornecer ao primeiro elemento xda primeira parte da tupla sem tentar avaliar as, e por isso sem tentar correspondência de padrões to10plus xscom (as,bs)na wherecláusula. Uma letcláusula teria feito a mesma coisa:
to10plus (x:xs) | x < 10 = let (as,bs) = to10plus xs in (x:as,1+bs)
| otherwise = ([], 0)
Como o @luqui aponta, essa é uma diferença no tempo das correspondências de padrões feitas por lete wheredeclarações:
let (a,b) = expr in body
-- OR --
body where (a,b) = expr
versus casedeclarações / definições de função:
case expr of (a,b) -> body
-- OR --
f (a,b) = body -- AND THEN EVALUATING: -- f expr
As instruções lete wherecorrespondem aos padrões preguiçosamente, o que significa que exprnão é correspondido ao padrão (a,b)até que aou bseja avaliado no body. Em contrapartida, para a caseafirmação, ela exprcorresponde (a,b)imediatamente, antes bodymesmo de ser examinada. E, dada a definição acima para f, o argumento to fserá correspondido (a,b)assim que a expressão f exprfor avaliada sem esperar até aou bser necessário na função body. Aqui estão alguns exemplos de trabalho para ilustrar:
ex1 = let (a,b) = undefined in print "okay"
ex2 = print "also okay" where (a,b) = undefined
ex3 = case undefined of (a,b) -> print "not okay"
ex4 = f undefined
f (a,b) = print "also not okay"
main = do
ex1 -- works
ex2 -- works
ex3 -- fails
ex4 -- fails
A adição ~altera o comportamento das casedefinições de / function para que a correspondência ocorra somente quando aou bfor necessária:
ex5 = case undefined of ~(a,b) -> print "works fine"
ex6 = g undefined
g ~(a,b) = print "also works fine"
ex7 = case undefined of ~(a,b) -> print $ "But trying " ++ show (a+b) ++ " would fail"
letewheresempre é preguiçoso, mas os padrões para argumentos são padronizados como estritos, a menos que sejam preguiçosos~.