Is there any useful reason why the GHC allows the following to block forever:
list = 1 : tail list
It seems with a bit of sophistication in the list iterator/generator we should be able to do something more useful:
- Return
error "Infinitely blocking list"
- Return
[1,1]
Explaining 2: it seems possible that when entering the generator to get element N
, we could then make all self references inside the generator limited to the list but ending at N-1
(we notice the read N
inside the scope generate N
and return the end-of-list). It's a sort of simple deadlock detection using scopes.
Clearly this isn't that useful for the toy example above, but it may allow for more useful/elegant finite, self-referential list definitions, for example:
primes = filter (\x -> none ((==0).mod x) primes) [2..]
Note that either change should only affect list generators that would currently result in an infinite-block, so they seem backward compatible language changes.
Ignoring the GHC-complexity required to make such a change for a moment, would this behavior break any existing language behavior that I am missing? Any other thoughts on the "elegance" of this change?
Also see another BFS example that could benefit below. To me, this seems more functional/elegant than some other solutions, since I am only needing to define what a bfsList is, not how to generate it (i.e specifying a terminating condition):
bfs :: (a -> Bool) -> (a -> [a]) -> [a] -> Maybe a
bfs predf expandf xs = find predf bfsList
where bfsList = xs ++ concatMap expandf bfsList