Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Tuple destructuring on infinite lists behaves differently when destructuring the Tuple as an argument than when destructuring using let

When trying to implement dropWhile using foldr the first algorithm that I came up with was this

dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' pred = fst . foldr (\cur (acc, xs) ->
    if pred cur
    then (acc, cur:xs)
    else (cur:xs, cur:xs)) ([], [])

While this works, it causes a stack overflow on infinite lists without giving any values. Because I wasn't sure, why this doesn't work, I just played around with the function until I came up with this:

dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' pred = fst . foldr (\cur t -> let (acc, xs) = t in
    if pred cur
    then (acc, cur:xs)
    else (cur:xs, cur:xs)) ([], [])

As you can see, the only difference between this one and the first is, that here I'm destructuring the tuple (acc, xs) inside a let binding instead of destructuring directly inside the function parameter. For some weird reason, this code works on infinite lists. If anyone has any idea why this behaves as it does, please let me know.

like image 796
Prophet Avatar asked Jun 12 '20 21:06

Prophet


1 Answers

Let expressions in Haskell are lazy:

Let Expressions

(...) Pattern bindings are matched lazily; an implicit ~ makes these patterns irrefutable.

In contrast, lambda abstractions desugar to case, thus making constructor patterns strict:

Curried applications and lambda abstractions

Translation: The following identity holds:

\ p1 … pn -> e = \ x1 … xn -> case (x1, …, xn) of (p1, …, pn) -> e

like image 66
Li-yao Xia Avatar answered Nov 15 '22 06:11

Li-yao Xia