Note that while Russel's Paradox helps to suggest that this might be non-computable, it still fails even if you change it to s = [e | e <- x, elem e s]
.
Here's an instructive manual expansion. For any non-empty list, x
s = [e | e <- x, not (e `elem` s)]
simplifies to
s = do e <- x
guard (not (e `elem` s))
return e
s = x >>= \e -> if (not (e `elem` s)) then return e else mzero
s = concatMap (\e -> if (not (e `elem` s)) then [e] else []) x
s = foldr ((++) . (\e -> if (not (e `elem` s)) then [e] else [])) [] x
s = foldr (\e xs -> if (not (e `elem` s)) then (e:xs) else xs) [] x
s = foldr (\e ys -> if (e `elem` s) then ys else (e:ys)) [] x
which we can then begin evaluating. Since x
was non-empty we can replace it with x:xs
and inline a foldr
let f = (\e ys -> if (e `elem` s) then ys else (e:ys))
s = f x (foldr f [] xs)
s = (\ys -> if (x `elem` s) then ys else (x:ys)) (foldr f [] xs)
s = (\ys -> if (x `elem` f x (foldr f [] xs)) then ys else (x:ys)) (foldr f [] xs)
which is where we have our infinite loop—in order to evaluate f x (foldr f [] xs)
we must evaluate f x (foldr f [] xs)
. You might say that the definition of s
is not "productive enough" to kickstart its self-recursion. Compare this to the trick fibs
definition
fibs = 1:1:zipWith (+) fibs (tail fibs)
which is kick-started with 1:1:...
in order to be "productive enough". In the case of s
, however, there's no (simple) way to be productive enough (see Will Ness' comment below for a fiendish workaround).
If we don't have the not there, it just switches the order of the branches on the if
, which we never reach anyway.