[Haskell-beginners] lazyness and tail recursion

Ovidiu Deac ovidiudeac at gmail.com
Mon Jul 25 09:53:20 CEST 2011


I've read the paragraph below from Real World Haskell
(http://book.realworldhaskell.org/read/efficient-file-processing-regular-expressions-and-file-name-matching.html
section "An important aside: writing lazy functions")

I can get a feeling why lazyness compensates for the lack of tail
recursion but I don't really understand why it would work in general.

My understanding is that that a function which returns a list with
non-tail recursion would only work this way with certain usage
patterns. In this case the caller of the funtion will have to use the
string in a stream-like fashion.

Can someone explain this better?

Thanks,
Ovidiu

PS: Here is the paragraph that I'm talking about:
"Looking at the globToRegex' function, we can see that it is not tail
recursive. To see why, examine its final clause again (several of its
other clauses are structured similarly). No comments

-- file: ch08/GlobRegex.hs
globToRegex' (c:cs) = escape c ++ globToRegex' cs

1 comment

It applies itself recursively, and the result of the recursive
application is used as a parameter to the (++) function. Since the
recursive application isn't the last thing the function does,
globToRegex' is not tail recursive. No comments

Why is our definition of this function not tail recursive? The answer
lies with Haskell's non-strict evaluation strategy. Before we start
talking about that, let's quickly talk about why, in a traditional
language, we'd try to avoid this kind of recursive definition. Here is
a simpler definition, of the (++) operator. It is recursivem, but not
tail recursive. 8 comments

-- file: ch08/append.hs
(++) :: [a] -> [a] -> [a]

(x:xs) ++ ys = x : (xs ++ ys)
[]     ++ ys = ys

No comments

In a strict language, if we evaluate "foo" ++ "bar", the entire list
is constructed, then returned. Non-strict evaluation defers much of
the work until it is needed. No comments

If we demand an element of the expression "foo" ++ "bar", the first
pattern of the function's definition matches, and we return the
expression x : (xs ++ ys). Because the (:) constructor is non-strict,
the evaluation of xs ++ ys can be deferred: we generate more elements
of the result at whatever rate they are demanded. When we generate
more of the result, we will no longer be using x, so the garbage
collector can reclaim it. Since we generate elements of the result on
demand, and do not hold onto parts that we are done with, the compiler
can evaluate our code in constant space."



More information about the Beginners mailing list