I’m reading through ‘A Gentle Introduction to Haskell,’ and early on it uses this example, which works fine in GHC and horribly in my brain:
initial = 0 next resp = resp process req = req+1 reqs = client initial resps resps = server reqs server (req:reqs) = process req : server reqs client initial ~(resp:resps) = initial : client (next resp) resps
And the calling code:
take 10 reqs
How I’m seeing it, is reqs is called, yielding a call to client with args 0 and resps. Thus wouldn’t resps now need to be called… which in turn calls reqs again? It all seems so infinite… if someone could detail how it’s actually working, I’d be most appreciative!
I find that it’s usually worthwhile to work out the behavior of small Haskell programs by hand. The evaluation rules are quite simple. The key thing to remember is that Haskell is non-strict (aka lazy): expressions are evaluated only when needed. Laziness is the reason seemingly infinite definitions can yield useful results. In this case, using
takemeans we will only need the first 10 elements of the infinite listreqs: they are all we ‘need’.In practical terms, ‘need’ is generally driven by pattern matches. E.g., a list expression will generally be evaluated up to the point where we can distinguish between
[]and(x:xs)before function application. (Note that a ‘~‘ preceding a pattern , as in the definition ofclient, makes it lazy (or irrefutable): a lazy pattern won’t force its argument until the whole expression is forced.)Remembering that
takeis:The evaluation of
take 10 reqslooks like: