Funky haskell lazy list implicit recursion -
in haskell, can build infinite lists due laziness:
prelude> let g = 4 : g prelude> g !! 0 4 prelude> take 10 g [4,4,4,4,4,4,4,4,4,4]
now, goes on when try construct list this?
prelude> let f = f !! 10 : f prelude> f !! 0 interrupted. prelude> take 10 f [interrupted. prelude>
the interrupted.
s me hitting ctrl+c after waiting few seconds. seems go infinite loop, why case?
explanation non-haskellers:
the :
operator prepend
:
prelude> 4 : [1, 2, 3] [4,1,2,3]
this line:
prelude> let g = 4 : g
says "let g
list constructed prepending 4
list g
". when ask first element, 4 returned, it's there. when ask second element, looks element after 4. element first element of list g
, calculated (4), 4
returned. next element second element of g
, again calculated, etc...
!!
indexing list, means element @ index 0
g
:
prelude> g !! 0 4
but when this:
prelude> let f = f !! 10 : f
something breaks, because compute first element of f
need 11th element, doesn't exist yet? expect exception, though, not infinite loop...
in case, picture may tell thousand words.
first, remember how cons (the (:)
list constructor) works. it's pair of 2 things: element, , reference list tail (which either cons, or []
).
as should know, when [1, 2, 3]
, it's shortcut (1:(2:(3:[])))
or 1:2:3:[]
. if visualize each cons pair box 2 slots, expression like:
┌───┬──┐ ┌───┬──┐ ┌───┬──┐ ┌────┐ │ 1 │ ─┼─>│ 2 │ ─┼─>│ 3 │ ─┼─>│ [] │ └───┴──┘ └───┴──┘ └───┴──┘ └────┘
one loop
when g = 4 : g
, you're not building "infinite" list, you're building circular list: g
defined cons tail reference points g
itself:
┌──────────┐ │ ┌───┬──┐ │ └>│ 4 │ ─┼─┘ └───┴──┘
this has nothing laziness, , self-reference: example, can same thing in (eager) common lisp using syntax '#1=(4 . #1#)
(where #1
g
).
whether g !! 0
, or g !! 1000000000000
, g
never grows: (!!)
runs around loop in-place, many times told to, until exhausts , returns element, 4
.
two loops
when f = (f !! 10) : f
, same thing happens—except now, element slot contains different expression 4
:
┌──────────┐ │ ┌───┬──┐ │ └>│ ╷ │ ─┼─┘ └─┼─┴──┘ │ │ ┌───────────┐ └>│ (f !! 10) │ └───────────┘
crucially, sub-expression also refers f
, tail does:
┌──────────┐ │ ┌───┬──┐ │ ┌┴>│ ╷ │ ─┼─┘ │ └─┼─┴──┘ │ │ │ │ ┌───────────┐ │ └>│ (f !! 10) │ │ └──┼────────┘ └─────────┘
so, when ask f !! n
, (!!)
first run around top loop n
times, return element, did g
. however, instead of escaping loop, (f !! 10)
re-enters it, , process repeats itself: looping 10 times around top, once around bottom, , back.
Comments
Post a Comment