[Haskell-cafe] MonadFix wiki confusion

Albert Y. C. Lai trebla at vex.net
Thu Oct 22 20:14:22 UTC 2015

On 2015-10-18 10:40 AM, Samuel Rødal wrote:
> the MonadFix wiki at https://wiki.haskell.org/MonadFix has a statement
> that I feel is a bit misleading.
> In section "2.2 Lazy algorithm interleaved with effects", it claims
> that making the BTree data structure strict doesn't cause endless
> recursion.
> Well, that's true, but that's just because rep_x_sum returns a tuple
> containing the BTree and the summed values of the current subtree, and
> the tuple is lazily constructed - postponing the construction of the
> tree value. So highlighting the fact that the function still works
> when the BTree structure is made strict is kind of a red herring.

This shows a wrong concept.

When you write this kind of code:

     return (x+y, a+b)

the tuple is not constructed lazily. The tuple is constructed here and now.

To be sure, the sub-value x+y is constructed lazily, but then you would 
say "x+y is constructed lazily", not "the tuple is constructed lazily". 
Similarly for a+b. And to say both at once, you would say "the tuple 
content is constructed lazily", not "the tuple is constructed lazily".

The tuple itself --- a machine word standing for the data constructor 
(,), followed by a pointer to the thunk for x+y, followed by a pointer 
to the thunk for a+b --- is constructed here and now.

Furthermore, if the code goes like this:

     return (B n x y, a+b)

then not only the tuple, but also the B n x y ("the tree value"), are 
constructed here and now.

Again, to be sure, this code is lazy on n, on x, and on y. But the tree 
value --- a machine word standing for the data constructor B, followed 
by a pointer to n, followed by a pointer to x, followed by a pointer to 
y --- is constructed here and now.

* * *

Guess what, neither can you fall back to "the content of the tuple, 
and/or the content of the tree value, is constructed lazily". Because it 
is readily refuted by an experiment.

And it should be natural to think up this experiment because every time 
you conjecture "this works because the code is lazy on xxx" the first 
instinct should be "so let me run an experiment in which I force 
evaluatation of xxx ASAP to test the conjecture".

So here it goes:

{-# LANGUAGE RecursiveDo #-}

import Control.Exception (evaluate)

data BTree = Z | B Int BTree BTree deriving Show

repsum t = do
     rec (u,s) <- rep_x_sum t s
     putStrLn ""
     return u
rep_x_sum Z _ = return (Z, 0)
rep_x_sum (B i l r) s = do
   putStr "("
   (l',sl) <- rep_x_sum l s
   putStr (show i)
   (r',sr) <- rep_x_sum r s
   putStr ")"
   evaluate l'
   evaluate r'
   tree <- evaluate (B s l' r')
   sum <- evaluate (i + sl + sr)
   tuple <- evaluate (tree, sum)
   return tuple

main = repsum (B 4 (B 3 Z Z) (B 5 Z (B 1 Z Z)))
        >>= print

This experiment hastens evaluation of the tuple content and most of the 
tree content; for good measure, evaluate the tuple and the tree, too, 
whether it is redundant or not.

Run this experiment. See it doesn't hang. Use this observation to refute 
many, many hypotheses.

The only content left unevaluated is s. Laziness in s is the only 
laziness needed. But then I already wrote that in the article, didn't I?

"The laziness needed is just in the rep_x_sum algorithm: it does not 
evaluate s."
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20151022/527e67e5/attachment.html>

More information about the Haskell-Cafe mailing list