I believe it is natural and useful in practice to implement black holes
as exceptions rather than divergence.
I wish it is natural in theory, too.
And some programming in Idleness...
> (define a (letrec [(x (delay (force x)))] x)) > (define b (letrec [(x (delay (print "hi") (force x)))] x)) > (define c (letrec [(x (delay (print "hi") 0))] x)) > (force a) force: reentrant promise (force a) force: reentrant promise > (force b) "hi" force: reentrant promise > (force b) force: reentrant promise > (force c) "hi" > (force c) >
> let a:int Lazy = let rec x = lazy (x.Force ()) in x;; > let b:int Lazy = let rec x = lazy (printfn "%s" "hi"; (x.Force ())) in x;; > let c:int Lazy = let rec x = lazy (printfn "%s" "hi"; 0) in x;; > a.Force ();; System.InvalidOperationException: a lazy value was accessed during its own initialization > b.Force ();; hi System.InvalidOperationException > b.Force ();; System.InvalidOperationException > c.Force ();; hi > c.Force ();; >
import Control.Monad.Fix main = do x <- mfix (\x -> do print "hi" >> print (x::Int) >> return x) print "bye" print x print "bye" print x "hi" Exception:loop
import Control.Monad.Fix main = do x <- mfix (\x -> do print "hi" >> return (x::Int)) print "bye" print x print "bye" print x "hi" "bye" Exception:loop
import Control.Monad.Fix main :: IO () main = do x <- (mfix (\x -> do print "hi" >> return 1)) print "bye" print x print "bye" print x "hi" "bye" 1 "bye" 1
> let a = let rec x = lazy (force x) in x > let b = let rec x = lazy (print_string "hi"; (force x)) in x > let c = let rec x = lazy (print_string "hi"; 0) in x > (force a) Exception: Lazy.Undefined. > (force a) Exception: Lazy.Undefined. > (force b) hi Exception: Lazy.Undefined. > (force b) Exception: Lazy.Undefined. > (force c) hi > (force c) >
> lazy val b:int = {println("hi"); b}
hi
hi
hi
hi
hi
...
StackOverflowError
> lazy val c = {println("hi"); 1}
hi
> c
>